-- Database schema for Music Band Information System

-- Create sequences for auto-generated IDs
CREATE SEQUENCE IF NOT EXISTS coordinates_seq START 1;
CREATE SEQUENCE IF NOT EXISTS location_seq START 1;
CREATE SEQUENCE IF NOT EXISTS album_seq START 1;
CREATE SEQUENCE IF NOT EXISTS person_seq START 1;
CREATE SEQUENCE IF NOT EXISTS music_band_seq START 1;

-- Create tables
CREATE TABLE IF NOT EXISTS coordinates (
    id BIGINT PRIMARY KEY DEFAULT nextval('coordinates_seq'),
    x BIGINT NOT NULL,
    y INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS location (
    id BIGINT PRIMARY KEY DEFAULT nextval('location_seq'),
    x INTEGER NOT NULL,
    y DOUBLE PRECISION NOT NULL,
    z INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS album (
    id BIGINT PRIMARY KEY DEFAULT nextval('album_seq'),
    name VARCHAR(255) NOT NULL CHECK (name <> ''),
    tracks BIGINT NOT NULL CHECK (tracks > 0)
);

CREATE TABLE IF NOT EXISTS person (
    id BIGINT PRIMARY KEY DEFAULT nextval('person_seq'),
    name VARCHAR(255) NOT NULL CHECK (name <> ''),
    eye_color VARCHAR(20) NOT NULL CHECK (eye_color IN ('GREEN', 'BLUE', 'BROWN')),
    hair_color VARCHAR(20) CHECK (hair_color IN ('GREEN', 'BLUE', 'BROWN')),
    location_id BIGINT REFERENCES location(id) ON DELETE CASCADE,
    height DOUBLE PRECISION NOT NULL CHECK (height > 0),
    nationality VARCHAR(20) NOT NULL CHECK (nationality IN ('INDIA', 'THAILAND', 'SOUTH_KOREA', 'NORTH_KOREA'))
);

CREATE TABLE IF NOT EXISTS music_band (
    id BIGINT PRIMARY KEY DEFAULT nextval('music_band_seq'),
    name VARCHAR(255) NOT NULL CHECK (name <> ''),
    coordinates_id BIGINT NOT NULL REFERENCES coordinates(id) ON DELETE CASCADE,
    creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    genre VARCHAR(30) NOT NULL CHECK (genre IN ('ROCK', 'PSYCHEDELIC_CLOUD_RAP', 'JAZZ')),
    number_of_participants BIGINT NOT NULL CHECK (number_of_participants > 0),
    singles_count INTEGER NOT NULL CHECK (singles_count > 0),
    description TEXT,
    best_album_id BIGINT REFERENCES album(id) ON DELETE SET NULL,
    albums_count INTEGER CHECK (albums_count IS NULL OR albums_count > 0),
    establishment_date TIMESTAMP WITH TIME ZONE NOT NULL,
    front_man_id BIGINT REFERENCES person(id) ON DELETE SET NULL
);

-- Create indexes for better performance
CREATE INDEX IF NOT EXISTS idx_music_band_name ON music_band(name);
CREATE INDEX IF NOT EXISTS idx_music_band_genre ON music_band(genre);
CREATE INDEX IF NOT EXISTS idx_music_band_participants ON music_band(number_of_participants);
CREATE INDEX IF NOT EXISTS idx_music_band_establishment_date ON music_band(establishment_date);

-- Database functions for special operations
CREATE OR REPLACE FUNCTION calculate_average_albums_count()
RETURNS DOUBLE PRECISION AS $$
BEGIN
    RETURN (SELECT AVG(albums_count) FROM music_band WHERE albums_count IS NOT NULL);
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION find_band_with_max_name()
RETURNS TABLE(id BIGINT, name VARCHAR(255)) AS $$
BEGIN
    RETURN QUERY
    SELECT mb.id, mb.name
    FROM music_band mb
    WHERE mb.name = (SELECT MAX(mb2.name) FROM music_band mb2)
    LIMIT 1;
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION group_bands_by_participants()
RETURNS TABLE(participants BIGINT, band_count BIGINT) AS $$
BEGIN
    RETURN QUERY
    SELECT mb.number_of_participants, COUNT(*)::BIGINT
    FROM music_band mb
    GROUP BY mb.number_of_participants
    ORDER BY mb.number_of_participants;
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION add_single_to_band(band_id BIGINT)
RETURNS VOID AS $$
BEGIN
    UPDATE music_band
    SET singles_count = singles_count + 1
    WHERE id = band_id;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'Band with ID % not found', band_id;
    END IF;
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION remove_participant_from_band(band_id BIGINT)
RETURNS VOID AS $$
BEGIN
    UPDATE music_band
    SET number_of_participants = number_of_participants - 1
    WHERE id = band_id AND number_of_participants > 1;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'Band with ID % not found or cannot remove participant (minimum 1 required)', band_id;
    END IF;
END;
$$ LANGUAGE plpgsql;