CREATE TYPE user_status AS ENUM ('active', 'inactive', 'blocked');
CREATE TYPE membership_status AS ENUM ('active', 'inactive', 'suspended', 'expired');
CREATE TYPE copy_status AS ENUM ('available', 'reserved', 'borrowed', 'lost', 'damaged');
CREATE TYPE payment_method AS ENUM ('cash', 'card', 'online');
CREATE TYPE payment_status AS ENUM ('pending', 'completed', 'failed', 'refunded');
CREATE TYPE reservation_status AS ENUM ('pending', 'active', 'cancelled', 'expired');
CREATE TYPE loan_status AS ENUM ('borrowed', 'returned', 'overdue', 'lost');
CREATE TYPE fine_status AS ENUM ('unpaid', 'paid', 'waived', 'overdue');
CREATE TYPE notification_status AS ENUM ('pending', 'sent', 'failed', 'read');
CREATE TYPE notification_type AS ENUM ('payment', 'fine', 'reservation', 'event', 'membership', 'general');
CREATE TYPE event_reservation_status AS ENUM ('reserved', 'cancelled', 'attended');
CREATE TYPE plan_type AS ENUM ('monthly', 'semiannual', 'annual');


CREATE TABLE app_user (
    user_id SERIAL PRIMARY KEY,
    username VARCHAR(20) NOT NULL UNIQUE,
    password VARCHAR(20) NOT NULL,
    email VARCHAR(30) NOT NULL UNIQUE,
    first_name VARCHAR(20) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    phone VARCHAR(15),
    status user_status NOT NULL DEFAULT 'active',
    address VARCHAR(50),
    street VARCHAR(50),
    city VARCHAR(20),
    country VARCHAR(50),
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    last_login TIMESTAMP,
    CONSTRAINT chk_app_user_email CHECK (email ~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$'),
    CONSTRAINT chk_app_user_phone CHECK (phone ~ '^\+389[0-9]{8}$')
);

CREATE TABLE admin (
    user_id INT PRIMARY KEY,
    FOREIGN KEY (user_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

CREATE TABLE member (
    user_id INT PRIMARY KEY,
    member_number INT NOT NULL UNIQUE,
    join_date DATE NOT NULL DEFAULT CURRENT_DATE,
    status membership_status NOT NULL DEFAULT 'inactive',
    FOREIGN KEY (user_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

CREATE TABLE librarian (
    user_id INT PRIMARY KEY,
    employee_code INT NOT NULL UNIQUE,
    hire_date DATE NOT NULL,
    FOREIGN KEY (user_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

CREATE TABLE publisher (
    publisher_id SERIAL PRIMARY KEY,
    name VARCHAR(30) NOT NULL UNIQUE,
    city VARCHAR(30),
    country VARCHAR(50)
);

CREATE TABLE category (
    category_id SERIAL PRIMARY KEY,
    name VARCHAR(30) NOT NULL UNIQUE,
    description TEXT,
    parent_category_id INT,
    FOREIGN KEY (parent_category_id) REFERENCES category(category_id)
        ON DELETE SET NULL
        ON UPDATE CASCADE
);

CREATE TABLE genre (
    genre_id SERIAL PRIMARY KEY,
    name VARCHAR(30) NOT NULL UNIQUE,
    description TEXT
);

CREATE TABLE author (
    author_id SERIAL PRIMARY KEY,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL
);

CREATE TABLE book (
    barcode VARCHAR(13) PRIMARY KEY,
    title VARCHAR(50) NOT NULL,
    publication_year INT,
    language VARCHAR(20),
    pages INT,
    description TEXT,
    publisher_id INT,
    shelf_location VARCHAR(10),
    FOREIGN KEY (publisher_id) REFERENCES publisher(publisher_id)
        ON DELETE SET NULL
        ON UPDATE CASCADE,
    CHECK (barcode ~ '^[0-9]{13}$')
);

CREATE TABLE book_copy (
    copy_id SERIAL PRIMARY KEY,
    barcode VARCHAR(13) NOT NULL,
    status copy_status NOT NULL DEFAULT 'available',
    FOREIGN KEY (barcode) REFERENCES book(barcode)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

CREATE TABLE book_author (
    barcode VARCHAR(13) NOT NULL,
    author_id INT NOT NULL,
    PRIMARY KEY (barcode, author_id),
    FOREIGN KEY (barcode) REFERENCES book(barcode)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
   FOREIGN KEY (author_id) REFERENCES author(author_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

CREATE TABLE book_genre (
    barcode VARCHAR(13) NOT NULL,
    genre_id INT NOT NULL,
    PRIMARY KEY (barcode, genre_id),
    FOREIGN KEY (barcode) REFERENCES book(barcode)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (genre_id) REFERENCES genre(genre_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

CREATE TABLE category_book (
    category_id INT NOT NULL,
    barcode VARCHAR(13) NOT NULL,
    PRIMARY KEY (category_id, barcode),
    FOREIGN KEY (category_id) REFERENCES category(category_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (barcode) REFERENCES book(barcode)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

CREATE TABLE membership_plan (
    plan_id SERIAL PRIMARY KEY,
    plan_name plan_type NOT NULL,
    duration_days INT NOT NULL,
    price INT NOT NULL,
    CONSTRAINT chk_duration_match CHECK (
        (plan_name = 'monthly' AND duration_days = 30) OR
        (plan_name = 'semiannual' AND duration_days = 180) OR
        (plan_name = 'annual' AND duration_days = 365)
    ),

    CONSTRAINT chk_price_match CHECK (
        (plan_name = 'monthly' AND price = 300) OR
        (plan_name = 'semiannual' AND price = 1500) OR
        (plan_name = 'annual' AND price = 2500)
    )
);

CREATE TABLE membership (
    membership_id SERIAL PRIMARY KEY,
    member_user_id INT NOT NULL,
    plan_id INT NOT NULL,
    start_date DATE NOT NULL,
    end_date DATE NOT NULL,
    status membership_status NOT NULL DEFAULT 'active',
    FOREIGN KEY (member_user_id) REFERENCES member(user_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (plan_id) REFERENCES membership_plan(plan_id)
        ON DELETE RESTRICT
        ON UPDATE CASCADE,
    CHECK (end_date >= start_date)
);

CREATE TABLE payment (
    payment_id SERIAL PRIMARY KEY,
    amount INT NOT NULL,
    payment_date DATE NOT NULL DEFAULT CURRENT_DATE,
    method payment_method NOT NULL,
    status payment_status NOT NULL DEFAULT 'pending',
    membership_id INT NOT NULL,
    member_user_id INT NOT NULL,
    FOREIGN KEY (membership_id) REFERENCES membership(membership_id)
        ON DELETE RESTRICT
        ON UPDATE CASCADE,
    FOREIGN KEY (member_user_id) REFERENCES member(user_id)
        ON DELETE RESTRICT
        ON UPDATE CASCADE
);


CREATE TABLE event (
	event_id serial4 NOT NULL,
	title varchar(50) NOT NULL,
	description text NULL,
	event_date date NOT NULL,
	start_time time NOT NULL,
	end_time time NOT NULL,
	"location" varchar(30) NOT NULL,
	max_seats int4 NOT NULL,
	available_seats int4 NOT NULL,
	created_by int4 NULL,
	CONSTRAINT event_check CHECK ((end_time > start_time)),
	CONSTRAINT event_check1 CHECK (((max_seats > 0) AND (available_seats >= 0) AND (available_seats <= max_seats))),
	CONSTRAINT event_pkey PRIMARY KEY (event_id),
	CONSTRAINT event_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.librarian(user_id) ON DELETE SET NULL ON UPDATE CASCADE
);

CREATE TABLE event_book (
    event_id INT NOT NULL,
    barcode VARCHAR(13) NOT NULL,
    PRIMARY KEY (event_id, barcode),
    FOREIGN KEY (event_id) REFERENCES event(event_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (barcode) REFERENCES book(barcode)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

CREATE TABLE event_reservation (
    event_reservation_id SERIAL PRIMARY KEY,
    event_id INT NOT NULL,
    member_user_id INT NOT NULL,
    reservation_date DATE NOT NULL DEFAULT CURRENT_DATE,
    seats_reserved INT NOT NULL DEFAULT 1,
    status event_reservation_status NOT NULL DEFAULT 'reserved',
    FOREIGN KEY (event_id) REFERENCES event(event_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (member_user_id) REFERENCES member(user_id)
        ON DELETE CASCADE
);

CREATE TABLE reservation (
    reservation_id SERIAL PRIMARY KEY,
    member_user_id INT NOT NULL,
    barcode VARCHAR(13) NOT NULL,
    reservation_date DATE NOT NULL DEFAULT CURRENT_DATE,
    reservation_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    expiration_date DATE,
    notified_at TIMESTAMP,
    status reservation_status NOT NULL DEFAULT 'pending',
    FOREIGN KEY (member_user_id) REFERENCES member(user_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (barcode) REFERENCES book(barcode)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    CHECK (expiration_date IS NULL OR expiration_date >= reservation_date)
);

CREATE TABLE loan_history (
    loan_id SERIAL PRIMARY KEY,
    copy_id INT NOT NULL,
    librarian_user_id INT NOT NULL,
    member_user_id INT NOT NULL,
    reservation_id INT,
    borrow_date DATE NOT NULL,
    due_date DATE NOT NULL,
    return_date DATE,
    status loan_status NOT NULL DEFAULT 'borrowed',
    FOREIGN KEY (copy_id) REFERENCES book_copy(copy_id)
        ON DELETE RESTRICT
        ON UPDATE CASCADE,
   FOREIGN KEY (librarian_user_id) REFERENCES librarian(user_id)
        ON DELETE RESTRICT
        ON UPDATE CASCADE,
    FOREIGN KEY (member_user_id) REFERENCES member(user_id)
        ON DELETE RESTRICT
        ON UPDATE CASCADE,
    FOREIGN KEY (reservation_id) REFERENCES reservation(reservation_id)
        ON DELETE SET NULL
        ON UPDATE CASCADE,
    CHECK (
        due_date >= borrow_date AND
        (return_date IS NULL OR return_date >= borrow_date)
    )
);

CREATE TABLE fines (
    fine_id SERIAL PRIMARY KEY,
    loan_id INT NOT NULL,
    amount INT NOT NULL,
    payment_due_date DATE NOT NULL,
    status fine_status NOT NULL DEFAULT 'unpaid',
    FOREIGN KEY (loan_id) REFERENCES loan_history(loan_id)
        ON DELETE RESTRICT
        ON UPDATE CASCADE
);

CREATE TABLE book_view_log (
    view_id SERIAL PRIMARY KEY,
    view_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    barcode VARCHAR(13),
    member_user_id INT,
    FOREIGN KEY (barcode) REFERENCES book(barcode)
        ON DELETE SET NULL
        ON UPDATE CASCADE,
    FOREIGN KEY (member_user_id) REFERENCES member(user_id)
        ON DELETE SET NULL
        ON UPDATE CASCADE
);

CREATE TABLE search_log (
    search_id SERIAL PRIMARY KEY,
    query_text TEXT NOT NULL,
    search_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    member_user_id INT,
    FOREIGN KEY (member_user_id) REFERENCES member(user_id)
        ON DELETE SET NULL
        ON UPDATE CASCADE
);

CREATE TABLE author_log (
    authorlog_id SERIAL PRIMARY KEY,
    member_user_id INT,
    query_text TEXT NOT NULL,
    log_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (member_user_id) REFERENCES member(user_id)
        ON DELETE SET NULL
        ON UPDATE CASCADE
);

CREATE TABLE authorlog_author (
    authorlog_id INT NOT NULL,
    author_id INT NOT NULL,
    PRIMARY KEY (authorlog_id, author_id),
    FOREIGN KEY (authorlog_id) REFERENCES author_log(authorlog_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (author_id) REFERENCES author(author_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

CREATE TABLE genre_log (
    genrelog_id SERIAL PRIMARY KEY,
    member_user_id INT,
    query_text TEXT NOT NULL,
    log_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (member_user_id) REFERENCES member(user_id)
        ON DELETE SET NULL
        ON UPDATE CASCADE
);

CREATE TABLE genrelog_genre (
    genrelog_id INT NOT NULL,
    genre_id INT NOT NULL,
    PRIMARY KEY (genrelog_id, genre_id),
    FOREIGN KEY (genrelog_id) REFERENCES genre_log(genrelog_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (genre_id) REFERENCES genre(genre_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

CREATE TABLE notification (
    notification_id SERIAL PRIMARY KEY,
    member_user_id INT NOT NULL,
    payment_id INT,
    notification_type notification_type NOT NULL,
    title VARCHAR(50) NOT NULL,
    message TEXT NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    sent_at TIMESTAMP,
    status notification_status NOT NULL DEFAULT 'pending',
    FOREIGN KEY (member_user_id) REFERENCES member(user_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (payment_id) REFERENCES payment(payment_id)
        ON DELETE SET NULL
        ON UPDATE CASCADE,
    CHECK (sent_at IS NULL OR sent_at >= created_at)
);

CREATE TABLE fines_notification (
    fine_id INT NOT NULL,
    notification_id INT NOT NULL,
    PRIMARY KEY (fine_id, notification_id),
    FOREIGN KEY (fine_id) REFERENCES fines(fine_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (notification_id) REFERENCES notification(notification_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);


