-- =========================================================
-- SKRYPT TWORZĄCY BAZĘ W POSTGRESQL
-- z tabelami: ARTYSTA, EKSPONAT, GALERIA, SALA, INSTYTUCJA,
--             WYPOZYCZENIE, MAGAZYNOWANIE, WYSTAWIENIE
-- + funkcja "ile_dni_wypozyczony(...)" licząca łączne dni w danym roku
--   (jeśli DATA_DO jest NULL, liczymy do CURRENT_DATE).
-- + trigger "check_wypozyczenie()" sprawdzający:
--    1) czy nie wypożyczamy eksponatu bezcennego
--    2) czy łączny czas poza muzeum nie przekroczy 30 dni w roku
--    3) czy nie wypożyczamy ostatniego dzieła artysty
--
-- DZIĘKI TEMU BAZA pilnuje reguł, nawet jeśli w DATA_DO jest NULL.
-- =========================================================

-- 1. Usuwamy tabele, jeśli istnieją:

-- 2. Tworzenie struktur:

CREATE TABLE ARTYSTA (
    ARTYSTA_ID      SERIAL        PRIMARY KEY,
    IMIE            VARCHAR(20)   NOT NULL,
    NAZWISKO        VARCHAR(20)   NOT NULL,
    ROK_URODZENIA   INT           NOT NULL,
    ROK_SMIERCI     INT           NULL,
    CONSTRAINT chk_rok_smierci CHECK (ROK_SMIERCI IS NULL OR ROK_URODZENIA <= ROK_SMIERCI)
);

CREATE TABLE EKSPONAT (
    EKSPONAT_ID     SERIAL        PRIMARY KEY,
    ARTYSTA_ID      INT           REFERENCES ARTYSTA(ARTYSTA_ID),
    TYTUL           VARCHAR(30)   NOT NULL,
    TYP             VARCHAR(20)   NOT NULL,
    WYSOKOSC        DECIMAL(6,2)  NOT NULL,
    SZEROKOSC       DECIMAL(6,2)  NOT NULL,
    WAGA            DECIMAL(6,2)  NOT NULL,
    CZY_CENNY       BOOLEAN       NOT NULL DEFAULT FALSE
);

CREATE TABLE GALERIA (
    GALERIA_ID   SERIAL       PRIMARY KEY,
    NAZWA        VARCHAR(30)  NOT NULL,
    MIASTO       VARCHAR(50),
    ADRES        VARCHAR(50)
);

CREATE TABLE SALA (
    SALA_ID      SERIAL         PRIMARY KEY,
    GALERIA_ID   INT            NOT NULL
        REFERENCES GALERIA(GALERIA_ID),
    NAZWA_SALI   VARCHAR(50)    NOT NULL
);

CREATE TABLE INSTYTUCJA (
    INSTYTUCJA_ID  SERIAL       PRIMARY KEY,
    NAZWA          VARCHAR(50)  NOT NULL,
    MIASTO         VARCHAR(50),
    ADRES          VARCHAR(50)
);

CREATE TABLE WYPOZYCZENIE (
    WYPOZYCZENIE_ID  SERIAL       PRIMARY KEY,
    EKSPONAT_ID      INT          NOT NULL
        REFERENCES EKSPONAT(EKSPONAT_ID),
    INSTYTUCJA_ID    INT          NOT NULL
        REFERENCES INSTYTUCJA(INSTYTUCJA_ID),
    DATA_OD          DATE         NOT NULL,
    DATA_DO          DATE,
    CONSTRAINT chk_wypozyczenie_dat CHECK (DATA_DO IS NULL OR DATA_OD <= DATA_DO)
);

CREATE TABLE MAGAZYNOWANIE (
    MAGAZYNOWANIE_ID SERIAL      PRIMARY KEY,
    DATA_OD          DATE        NOT NULL,
    DATA_DO          DATE,
    EKSPONAT_ID      INT         NOT NULL
        REFERENCES EKSPONAT(EKSPONAT_ID),
    CONSTRAINT chk_magazynowanie_dat CHECK (DATA_DO IS NULL OR DATA_OD <= DATA_DO)
);

CREATE TABLE WYSTAWIENIE (
    WYSTAWIENIE_ID SERIAL        PRIMARY KEY,
    EKSPONAT_ID    INT           NOT NULL
        REFERENCES EKSPONAT(EKSPONAT_ID),
    GALERIA_ID     INT           NOT NULL
        REFERENCES GALERIA(GALERIA_ID),
    DATA_OD        DATE          NOT NULL,
    DATA_DO        DATE,
    CONSTRAINT chk_wystawienie_dat CHECK (DATA_DO IS NULL OR DATA_OD <= DATA_DO)
);

-- ===========================================
-- FUNKCJA: ile_dni_wypozyczony(eksponat, rok)
-- liczy łączną liczbę dni wypożyczenia w danym roku:
-- jeśli DATA_DO IS NULL -> liczymy do CURRENT_DATE.
-- ===========================================
CREATE OR REPLACE FUNCTION ile_dni_wypozyczony(
    eksponat INT,
    rok INT
)
RETURNS INT
LANGUAGE plpgsql
AS $$
DECLARE
    suma_dni INT := 0;
    w RECORD;
    diff INT;
BEGIN
    FOR w IN
        SELECT data_od, data_do
          FROM WYPOZYCZENIE
         WHERE eksponat_id = eksponat
           AND EXTRACT(YEAR FROM data_od) = rok
    LOOP
        IF w.data_do IS NULL THEN
            diff := CURRENT_DATE - w.data_od;
        ELSE
            diff := w.data_do - w.data_od;
        END IF;
        IF diff < 0 THEN
            diff := 0;  -- w razie złych dat
        END IF;
        suma_dni := suma_dni + diff;
    END LOOP;

    RETURN suma_dni;
END;
$$;

-- ===========================================
-- FUNKCJA TRIGGERA: check_wypozyczenie()
-- Sprawdza:
-- 1) czy eksponat nie jest bezcenny
-- 2) czy łącznie nie przekroczy 30 dni w roku
-- 3) czy nie wypożyczamy ostatniego eksponatu artysty
-- ===========================================
CREATE OR REPLACE FUNCTION check_wypozyczenie()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
DECLARE
    cenny          BOOLEAN;
    v_artysta_id   INT;
    rok            INT;
    new_diff       INT;       
    sum_days       INT;       
    count_in_museum INT;
BEGIN
    -- 1) Czy eksponat jest bezcenny?
    SELECT e.czy_cenny, e.artysta_id
      INTO cenny, v_artysta_id
      FROM EKSPONAT e
     WHERE e.eksponat_id = NEW.eksponat_id;

    IF cenny THEN
       RAISE EXCEPTION 'Nie można wypożyczyć eksponatu bezcennego!';
    END IF;

    -- 2) Sprawdzenie limitu 30 dni w roku
    rok := EXTRACT(YEAR FROM NEW.data_od);

    IF NEW.data_do IS NULL THEN
        new_diff := CURRENT_DATE - NEW.data_od;
    ELSE
        new_diff := NEW.data_do - NEW.data_od;
    END IF;
    IF new_diff < 0 THEN
       new_diff := 0;
    END IF;

    sum_days := ile_dni_wypozyczony(NEW.eksponat_id, rok);

    IF (sum_days + new_diff) > 30 THEN
       RAISE EXCEPTION 'Przekroczono 30 dni wypożyczenia w roku %, eksponat_id=%!', rok, NEW.eksponat_id;
    END IF;

    -- 3) Ostatni eksponat artysty w muzeum
    IF v_artysta_id IS NOT NULL THEN
       SELECT COUNT(*)
         INTO count_in_museum
         FROM EKSPONAT ex
        WHERE ex.artysta_id = v_artysta_id
          AND ex.eksponat_id NOT IN (
               SELECT w.eksponat_id
                 FROM WYPOZYCZENIE w
                WHERE w.data_do IS NULL 
                   OR w.data_do >= CURRENT_DATE
          );

       IF count_in_museum <= 1 THEN
         RAISE EXCEPTION 'Nie można wypożyczyć eksponatu - byłby to ostatni % w muzeum!', v_artysta_id;
       END IF;
    END IF;

    RETURN NEW;
END;
$$;

-- TRIGGER
CREATE TRIGGER trig_check_wypozyczenie
BEFORE INSERT OR UPDATE
ON WYPOZYCZENIE
FOR EACH ROW
EXECUTE FUNCTION check_wypozyczenie();