use super::error::DatabaseError;

pub(crate) const SCHEMA_VERSION: u32 = 10;

pub struct Table {
    pub(crate) name: &'static str,
    pub(crate) schema: &'static str,
    pub(crate) indexes: &'static [&'static str],
    pub(crate) triggers: &'static [&'static str],
}

pub fn init(conn: &mut rusqlite::Connection, tables: &[Table]) -> Result<(), DatabaseError> {
    let trans = conn.transaction()?;

    for table in tables {
        if !trans.table_exists(None, table.name)? {
            trans.execute(table.schema, [])?;

            for &index in table.indexes {
                trans.execute(index, [])?;
            }

            for &trigger in table.triggers {
                trans.execute(trigger, [])?;
            }
            log::debug!("table {} created", table.name);
        }
    }

    trans.commit()?;
    Ok(())
}

pub const LIBRARIES: Table = Table {
    name: "libraries",
    schema: r"
    CREATE TABLE libraries (
        id          INTEGER PRIMARY KEY ASC NOT NULL,
        name        TEXT NOT NULL,
        root_path   TEXT NOT NULL UNIQUE,
        media_type  TEXT NOT NULL CHECK(media_type IN ('movie', 'show')),
        created_at  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
    );",
    indexes: &[],
    triggers: &[],
};

pub const HOST: Table = Table {
    name: "host",
    schema: r"
    CREATE TABLE host (
        key     TEXT UNIQUE PRIMARY KEY NOT NULL,
        value   ANY NOT NULL
    );",
    indexes: &[],
    triggers: &[],
};

pub const MOVIES: Table = Table {
    name: "movies",
    schema: r"
    CREATE TABLE movies (
        id                  INTEGER PRIMARY KEY ASC NOT NULL,
        library_id          INTEGER NOT NULL REFERENCES libraries(id),
        tmdb_id             INTEGER,
        title               TEXT NOT NULL,
        original_title      TEXT,
        file                TEXT NOT NULL,
        release_year        INTEGER,
        description         TEXT,
        rating              REAL,
        watched             INTEGER NOT NULL DEFAULT 0,
        skip_auto_match     INTEGER NOT NULL DEFAULT 0,
        playback_position   REAL,
        last_playback       TIMESTAMP,
        created_at          TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at          TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
    );",
    indexes: &[
        "CREATE INDEX idx_movies_file ON movies(file);",
        "CREATE INDEX idx_movies_tmdb_id ON movies(tmdb_id);",
        "CREATE INDEX idx_movies_library_id ON movies(library_id);",
        "CREATE INDEX idx_movies_last_playback ON movies(last_playback) WHERE last_playback IS NOT NULL;",
        "CREATE INDEX idx_movies_created_at ON movies(created_at DESC);",
    ],
    triggers: &[r"
        CREATE TRIGGER cleanup_media_meta_after_movie_delete
        AFTER DELETE on movies
        BEGIN
            DELETE FROM media_meta
            WHERE file = (SELECT root_path FROM libraries WHERE id = OLD.library_id) || '/' || OLD.file
              AND NOT EXISTS (
                  SELECT 1 FROM movies m JOIN libraries l ON l.id = m.library_id
                  WHERE l.root_path || '/' || m.file = media_meta.file
              );
        END;"],
};

pub const IMAGES: Table = Table {
    name: "images",
    schema: r"
    CREATE TABLE images (
        id              INTEGER PRIMARY KEY ASC NOT NULL,
        image           BLOB NOT NULL,
        filename        TEXT NOT NULL,
        mime_type       TEXT NOT NULL,
        size            INTEGER NOT NULL,
        created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
    );",
    indexes: &[],
    triggers: &[],
};

pub const MOVIE_IMAGES: Table = Table {
    name: "movie_images",
    schema: r"
    CREATE TABLE movie_images (
        movie_id    INTEGER NOT NULL REFERENCES movies(id) ON DELETE CASCADE,
        image_id    INTEGER NOT NULL REFERENCES images(id),
        type        TEXT NOT NULL,
        PRIMARY KEY (movie_id, image_id)
    );",
    indexes: &["CREATE INDEX idx_movie_images_movie_type ON movie_images(movie_id, type);"],
    triggers: &[],
};

pub const SHOWS: Table = Table {
    name: "shows",
    schema: r"
    CREATE TABLE shows (
        id              INTEGER PRIMARY KEY ASC NOT NULL,
        tmdb_id         INTEGER,
        title           TEXT NOT NULL,
        original_title  TEXT,
        release_year    INTEGER,
        description     TEXT,
        rating          REAL,
        watched         INTEGER NOT NULL DEFAULT 0,
        skip_auto_match INTEGER NOT NULL DEFAULT 0,
        created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
    );
    ",
    indexes: &[
        "CREATE INDEX idx_shows_title ON shows(title);",
        "CREATE INDEX idx_shows_tmdb_id ON shows(tmdb_id);",
    ],
    triggers: &[],
};

pub const SEASONS: Table = Table {
    name: "seasons",
    schema: r"
    CREATE TABLE seasons (
        id              INTEGER PRIMARY KEY ASC NOT NULL,
        show_id         INTEGER NOT NULL REFERENCES shows(id) ON DELETE CASCADE,
        tmdb_id         INTEGER,
        season_number   INTEGER NOT NULL,
        title           TEXT,
        release_year    INTEGER,
        description     TEXT,
        rating          REAL,
        watched         INTEGER NOT NULL DEFAULT 0,
        skip_auto_match INTEGER NOT NULL DEFAULT 0,
        created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        UNIQUE(show_id, season_number)
    );
    ",
    indexes: &[
        "CREATE INDEX idx_seasons_tmdb_id ON seasons(tmdb_id);",
        "CREATE INDEX idx_seasons_created_at ON seasons(created_at DESC);",
    ],
    triggers: &[],
};

pub const EPISODES: Table = Table {
    name: "episodes",
    schema: r"
    CREATE TABLE episodes (
        id                  INTEGER PRIMARY KEY ASC NOT NULL,
        season_id           INTEGER NOT NULL REFERENCES seasons(id) ON DELETE CASCADE,
        library_id          INTEGER NOT NULL REFERENCES libraries(id),
        tmdb_id             INTEGER,
        episode_number      INTEGER NOT NULL,
        title               TEXT,
        file                TEXT NOT NULL,
        watched             INTEGER NOT NULL DEFAULT 0,
        playback_position   REAL,
        last_playback       TIMESTAMP,
        created_at          TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at          TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        UNIQUE(season_id, episode_number)
    );
    ",
    indexes: &[
        "CREATE INDEX idx_episodes_library_id ON episodes(library_id);",
        "CREATE INDEX idx_episodes_last_playback ON episodes(last_playback) WHERE last_playback IS NOT NULL;",
    ],
    triggers: &[r"
            CREATE TRIGGER cleanup_media_meta_after_episode_delete
            AFTER DELETE on episodes
            BEGIN
                DELETE FROM media_meta
                WHERE file = (SELECT root_path FROM libraries WHERE id = OLD.library_id) || '/' || OLD.file
                  AND NOT EXISTS (
                      SELECT 1 FROM episodes e JOIN libraries l ON l.id = e.library_id
                      WHERE l.root_path || '/' || e.file = media_meta.file
                  );
            END;"],
};

pub const SHOW_IMAGES: Table = Table {
    name: "show_images",
    schema: r"
    CREATE TABLE show_images (
        show_id     INTEGER NOT NULL REFERENCES shows(id) ON DELETE CASCADE,
        image_id    INTEGER NOT NULL REFERENCES images(id),
        type        TEXT NOT NULL,
        PRIMARY KEY (show_id, image_id)
    );
    ",
    indexes: &["CREATE INDEX idx_show_images_show_type ON show_images(show_id, type);"],
    triggers: &[],
};

pub const SEASON_IMAGES: Table = Table {
    name: "season_images",
    schema: r"
    CREATE TABLE season_images (
        season_id   INTEGER NOT NULL REFERENCES seasons(id) ON DELETE CASCADE,
        image_id    INTEGER NOT NULL REFERENCES images(id),
        type        TEXT NOT NULL,
        PRIMARY KEY (season_id, image_id)
    );
    ",
    indexes: &["CREATE INDEX idx_season_images_season_type ON season_images(season_id, type);"],
    triggers: &[],
};

pub const MEDIA_META: Table = Table {
    name: "media_meta",
    schema: r"
    CREATE TABLE media_meta (
        id          INTEGER PRIMARY KEY,
        file        TEXT NOT NULL UNIQUE,
        duration    REAL,
        created_at  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
    );
    ",
    indexes: &[],
    triggers: &[],
};
