-- +goose Up

-- ---------------------------------------------------------------------------
-- Extensions
-- ---------------------------------------------------------------------------
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS pg_trgm;

-- ---------------------------------------------------------------------------
-- UUID v7 generator (time-ordered, millisecond precision).
-- Structure: 48-bit unix_ts_ms | 4-bit version (0111) | 12-bit rand_a | 2-bit variant (10) | 62-bit rand_b
-- ---------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION uuid_generate_v7()
RETURNS uuid
AS $$
DECLARE
    unix_ts_ms bytea;
    uuid_bytes bytea;
BEGIN
    -- 48-bit Unix timestamp in milliseconds.
    -- int8send produces 8 big-endian bytes; skip the first 2 zero bytes to get 6.
    unix_ts_ms = substring(int8send(floor(extract(epoch from clock_timestamp()) * 1000)::bigint) from 3);

    -- Use a random v4 UUID as the source of random bits for rand_a and rand_b.
    uuid_bytes = uuid_send(gen_random_uuid());

    -- Overwrite bytes 0-5 with the timestamp (positions 1-6 in 1-indexed bytea).
    uuid_bytes = overlay(uuid_bytes placing unix_ts_ms from 1 for 6);

    -- Set version nibble (bits 48-51) to 0111 (7).
    uuid_bytes = set_bit(uuid_bytes, 48, 0);
    uuid_bytes = set_bit(uuid_bytes, 49, 1);
    uuid_bytes = set_bit(uuid_bytes, 50, 1);
    uuid_bytes = set_bit(uuid_bytes, 51, 1);

    -- Variant bits (64-65) stay at 10 as inherited from gen_random_uuid().

    RETURN encode(uuid_bytes, 'hex')::uuid;
END
$$ LANGUAGE plpgsql VOLATILE;

-- ---------------------------------------------------------------------------
-- Enums
-- ---------------------------------------------------------------------------
CREATE TYPE user_plan       AS ENUM ('free', 'paid');
CREATE TYPE user_gender     AS ENUM ('male', 'female');
CREATE TYPE user_goal       AS ENUM ('lose', 'maintain', 'gain');
CREATE TYPE activity_level  AS ENUM ('low', 'moderate', 'high');
CREATE TYPE recipe_source   AS ENUM ('spoonacular', 'ai', 'user');
CREATE TYPE recipe_difficulty AS ENUM ('easy', 'medium', 'hard');

-- ---------------------------------------------------------------------------
-- users
-- ---------------------------------------------------------------------------
CREATE TABLE users (
    id               UUID         PRIMARY KEY DEFAULT uuid_generate_v7(),
    firebase_uid     VARCHAR(128) NOT NULL UNIQUE,
    email            VARCHAR(255) NOT NULL,
    name             VARCHAR(255) NOT NULL DEFAULT '',
    avatar_url       TEXT,
    height_cm        SMALLINT,
    weight_kg        DECIMAL(5,2),
    date_of_birth    DATE,
    gender           user_gender,
    activity         activity_level,
    goal             user_goal,
    daily_calories   INTEGER,
    plan             user_plan    NOT NULL DEFAULT 'free',
    preferences      JSONB        NOT NULL DEFAULT '{}'::jsonb,
    refresh_token    TEXT,
    token_expires_at TIMESTAMPTZ,
    created_at       TIMESTAMPTZ  NOT NULL DEFAULT now(),
    updated_at       TIMESTAMPTZ  NOT NULL DEFAULT now()
);
CREATE INDEX idx_users_firebase_uid ON users (firebase_uid);
CREATE INDEX idx_users_email        ON users (email);

-- ---------------------------------------------------------------------------
-- languages
-- ---------------------------------------------------------------------------
CREATE TABLE languages (
    code         VARCHAR(10) PRIMARY KEY,
    native_name  TEXT        NOT NULL,
    english_name TEXT        NOT NULL,
    is_active    BOOLEAN     NOT NULL DEFAULT true,
    sort_order   SMALLINT    NOT NULL DEFAULT 0
);

-- ---------------------------------------------------------------------------
-- units + unit_translations
-- ---------------------------------------------------------------------------
CREATE TABLE units (
    code       VARCHAR(20) PRIMARY KEY,
    sort_order SMALLINT    NOT NULL DEFAULT 0
);

CREATE TABLE unit_translations (
    unit_code  VARCHAR(20) NOT NULL REFERENCES units(code) ON DELETE CASCADE,
    lang       VARCHAR(10) NOT NULL,
    name       TEXT        NOT NULL,
    PRIMARY KEY (unit_code, lang)
);

-- ---------------------------------------------------------------------------
-- ingredient_categories + translations
-- ---------------------------------------------------------------------------
CREATE TABLE ingredient_categories (
    slug       VARCHAR(50) PRIMARY KEY,
    sort_order SMALLINT    NOT NULL DEFAULT 0
);

CREATE TABLE ingredient_category_translations (
    category_slug VARCHAR(50) NOT NULL REFERENCES ingredient_categories(slug) ON DELETE CASCADE,
    lang          VARCHAR(10) NOT NULL,
    name          TEXT        NOT NULL,
    PRIMARY KEY (category_slug, lang)
);

-- ---------------------------------------------------------------------------
-- ingredients (canonical catalog — formerly ingredient_mappings)
-- ---------------------------------------------------------------------------
CREATE TABLE ingredients (
    id                UUID         PRIMARY KEY DEFAULT uuid_generate_v7(),
    canonical_name    VARCHAR(255) NOT NULL,
    category          VARCHAR(50)  REFERENCES ingredient_categories(slug),
    default_unit      VARCHAR(20)  REFERENCES units(code),
    calories_per_100g DECIMAL(8,2),
    protein_per_100g  DECIMAL(8,2),
    fat_per_100g      DECIMAL(8,2),
    carbs_per_100g    DECIMAL(8,2),
    fiber_per_100g    DECIMAL(8,2),
    storage_days      INTEGER,
    created_at        TIMESTAMPTZ  NOT NULL DEFAULT now(),
    updated_at        TIMESTAMPTZ  NOT NULL DEFAULT now(),
    CONSTRAINT uq_ingredient_canonical_name UNIQUE (canonical_name)
);
CREATE INDEX idx_ingredients_canonical_name ON ingredients (canonical_name);
CREATE INDEX idx_ingredients_category       ON ingredients (category);

-- ---------------------------------------------------------------------------
-- ingredient_translations
-- ---------------------------------------------------------------------------
CREATE TABLE ingredient_translations (
    ingredient_id UUID         NOT NULL REFERENCES ingredients(id) ON DELETE CASCADE,
    lang          VARCHAR(10)  NOT NULL,
    name          VARCHAR(255) NOT NULL,
    PRIMARY KEY (ingredient_id, lang)
);
CREATE INDEX idx_ingredient_translations_ingredient_id ON ingredient_translations (ingredient_id);

-- ---------------------------------------------------------------------------
-- ingredient_aliases (relational, replaces JSONB aliases column)
-- ---------------------------------------------------------------------------
CREATE TABLE ingredient_aliases (
    ingredient_id UUID        NOT NULL REFERENCES ingredients(id) ON DELETE CASCADE,
    lang          VARCHAR(10) NOT NULL,
    alias         TEXT        NOT NULL,
    PRIMARY KEY (ingredient_id, lang, alias)
);
CREATE INDEX idx_ingredient_aliases_lookup ON ingredient_aliases (ingredient_id, lang);
CREATE INDEX idx_ingredient_aliases_trgm   ON ingredient_aliases USING GIN (alias gin_trgm_ops);

-- ---------------------------------------------------------------------------
-- cuisines + cuisine_translations
-- ---------------------------------------------------------------------------
CREATE TABLE cuisines (
    slug       VARCHAR(50) PRIMARY KEY,
    name       TEXT        NOT NULL,
    sort_order SMALLINT    NOT NULL DEFAULT 0
);

CREATE TABLE cuisine_translations (
    cuisine_slug VARCHAR(50) NOT NULL REFERENCES cuisines(slug) ON DELETE CASCADE,
    lang         VARCHAR(10) NOT NULL,
    name         TEXT        NOT NULL,
    PRIMARY KEY  (cuisine_slug, lang)
);

-- ---------------------------------------------------------------------------
-- tags + tag_translations
-- ---------------------------------------------------------------------------
CREATE TABLE tags (
    slug       VARCHAR(100) PRIMARY KEY,
    name       TEXT         NOT NULL,
    sort_order SMALLINT     NOT NULL DEFAULT 0
);

CREATE TABLE tag_translations (
    tag_slug   VARCHAR(100) NOT NULL REFERENCES tags(slug) ON DELETE CASCADE,
    lang       VARCHAR(10)  NOT NULL,
    name       TEXT         NOT NULL,
    PRIMARY KEY (tag_slug, lang)
);

-- ---------------------------------------------------------------------------
-- dish_categories + dish_category_translations
-- ---------------------------------------------------------------------------
CREATE TABLE dish_categories (
    slug       VARCHAR(50) PRIMARY KEY,
    name       TEXT        NOT NULL,
    sort_order SMALLINT    NOT NULL DEFAULT 0
);

CREATE TABLE dish_category_translations (
    category_slug VARCHAR(50) NOT NULL REFERENCES dish_categories(slug) ON DELETE CASCADE,
    lang          VARCHAR(10) NOT NULL,
    name          TEXT        NOT NULL,
    PRIMARY KEY   (category_slug, lang)
);

-- ---------------------------------------------------------------------------
-- dishes + dish_translations + dish_tags
-- ---------------------------------------------------------------------------
CREATE TABLE dishes (
    id            UUID         PRIMARY KEY DEFAULT uuid_generate_v7(),
    cuisine_slug  VARCHAR(50)  REFERENCES cuisines(slug)        ON DELETE SET NULL,
    category_slug VARCHAR(50)  REFERENCES dish_categories(slug) ON DELETE SET NULL,
    name          TEXT         NOT NULL,
    description   TEXT,
    image_url     TEXT,
    avg_rating    DECIMAL(3,2) NOT NULL DEFAULT 0.0,
    review_count  INTEGER      NOT NULL DEFAULT 0,
    created_at    TIMESTAMPTZ  NOT NULL DEFAULT now(),
    updated_at    TIMESTAMPTZ  NOT NULL DEFAULT now()
);
CREATE INDEX idx_dishes_cuisine  ON dishes (cuisine_slug);
CREATE INDEX idx_dishes_category ON dishes (category_slug);
CREATE INDEX idx_dishes_rating   ON dishes (avg_rating DESC);

CREATE TABLE dish_translations (
    dish_id     UUID        NOT NULL REFERENCES dishes(id) ON DELETE CASCADE,
    lang        VARCHAR(10) NOT NULL,
    name        TEXT        NOT NULL,
    description TEXT,
    PRIMARY KEY (dish_id, lang)
);

CREATE TABLE dish_tags (
    dish_id  UUID         NOT NULL REFERENCES dishes(id)  ON DELETE CASCADE,
    tag_slug VARCHAR(100) NOT NULL REFERENCES tags(slug)  ON DELETE CASCADE,
    PRIMARY KEY (dish_id, tag_slug)
);

-- ---------------------------------------------------------------------------
-- recipes (one cooking variant of a dish)
-- ---------------------------------------------------------------------------
CREATE TABLE recipes (
    id                   UUID              PRIMARY KEY DEFAULT uuid_generate_v7(),
    dish_id              UUID              NOT NULL REFERENCES dishes(id) ON DELETE CASCADE,
    source               recipe_source     NOT NULL DEFAULT 'ai',
    difficulty           recipe_difficulty,
    prep_time_min        INTEGER,
    cook_time_min        INTEGER,
    servings             SMALLINT,
    calories_per_serving DECIMAL(8,2),
    protein_per_serving  DECIMAL(8,2),
    fat_per_serving      DECIMAL(8,2),
    carbs_per_serving    DECIMAL(8,2),
    fiber_per_serving    DECIMAL(8,2),
    created_at           TIMESTAMPTZ  NOT NULL DEFAULT now(),
    updated_at           TIMESTAMPTZ  NOT NULL DEFAULT now()
);
CREATE INDEX idx_recipes_dish_id    ON recipes (dish_id);
CREATE INDEX idx_recipes_difficulty ON recipes (difficulty);
CREATE INDEX idx_recipes_prep_time  ON recipes (prep_time_min);
CREATE INDEX idx_recipes_calories   ON recipes (calories_per_serving);
CREATE INDEX idx_recipes_source     ON recipes (source);

-- ---------------------------------------------------------------------------
-- recipe_translations (per-language cooking notes only)
-- ---------------------------------------------------------------------------
CREATE TABLE recipe_translations (
    recipe_id UUID        NOT NULL REFERENCES recipes(id) ON DELETE CASCADE,
    lang      VARCHAR(10) NOT NULL,
    notes     TEXT,
    PRIMARY KEY (recipe_id, lang)
);

-- ---------------------------------------------------------------------------
-- recipe_ingredients + recipe_ingredient_translations
-- ---------------------------------------------------------------------------
CREATE TABLE recipe_ingredients (
    id            UUID          PRIMARY KEY DEFAULT uuid_generate_v7(),
    recipe_id     UUID          NOT NULL REFERENCES recipes(id)     ON DELETE CASCADE,
    ingredient_id UUID          REFERENCES ingredients(id)          ON DELETE SET NULL,
    name          TEXT          NOT NULL,
    amount        DECIMAL(10,2) NOT NULL DEFAULT 0,
    unit_code     VARCHAR(20),
    is_optional   BOOLEAN       NOT NULL DEFAULT false,
    sort_order    SMALLINT      NOT NULL DEFAULT 0
);
CREATE INDEX idx_recipe_ingredients_recipe_id ON recipe_ingredients (recipe_id);

CREATE TABLE recipe_ingredient_translations (
    ri_id UUID        NOT NULL REFERENCES recipe_ingredients(id) ON DELETE CASCADE,
    lang  VARCHAR(10) NOT NULL,
    name  TEXT        NOT NULL,
    PRIMARY KEY (ri_id, lang)
);

-- ---------------------------------------------------------------------------
-- recipe_steps + recipe_step_translations
-- ---------------------------------------------------------------------------
CREATE TABLE recipe_steps (
    id            UUID     PRIMARY KEY DEFAULT uuid_generate_v7(),
    recipe_id     UUID     NOT NULL REFERENCES recipes(id) ON DELETE CASCADE,
    step_number   SMALLINT NOT NULL,
    timer_seconds INTEGER,
    image_url     TEXT,
    description   TEXT     NOT NULL,
    UNIQUE (recipe_id, step_number)
);
CREATE INDEX idx_recipe_steps_recipe_id ON recipe_steps (recipe_id);

CREATE TABLE recipe_step_translations (
    step_id     UUID        NOT NULL REFERENCES recipe_steps(id) ON DELETE CASCADE,
    lang        VARCHAR(10) NOT NULL,
    description TEXT        NOT NULL,
    PRIMARY KEY (step_id, lang)
);

-- ---------------------------------------------------------------------------
-- products (user fridge / pantry items)
-- ---------------------------------------------------------------------------
CREATE TABLE products (
    id                    UUID          PRIMARY KEY DEFAULT uuid_generate_v7(),
    user_id               UUID          NOT NULL REFERENCES users(id)       ON DELETE CASCADE,
    primary_ingredient_id UUID          REFERENCES ingredients(id),
    name                  TEXT          NOT NULL,
    quantity              DECIMAL(10,2) NOT NULL DEFAULT 1,
    unit                  TEXT          NOT NULL DEFAULT 'pcs' REFERENCES units(code),
    category              TEXT,
    storage_days          INT           NOT NULL DEFAULT 7,
    added_at              TIMESTAMPTZ   NOT NULL DEFAULT now()
);
CREATE INDEX idx_products_user_id ON products (user_id);

-- ---------------------------------------------------------------------------
-- product_ingredients (M2M: composite product ↔ ingredients)
-- ---------------------------------------------------------------------------
CREATE TABLE product_ingredients (
    product_id      UUID          NOT NULL REFERENCES products(id)    ON DELETE CASCADE,
    ingredient_id   UUID          NOT NULL REFERENCES ingredients(id) ON DELETE CASCADE,
    amount_per_100g DECIMAL(10,2),
    PRIMARY KEY (product_id, ingredient_id)
);

-- ---------------------------------------------------------------------------
-- user_saved_recipes (thin bookmark — content lives in dishes + recipes)
-- ---------------------------------------------------------------------------
CREATE TABLE user_saved_recipes (
    id        UUID        PRIMARY KEY DEFAULT uuid_generate_v7(),
    user_id   UUID        NOT NULL REFERENCES users(id)   ON DELETE CASCADE,
    recipe_id UUID        NOT NULL REFERENCES recipes(id) ON DELETE CASCADE,
    saved_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (user_id, recipe_id)
);
CREATE INDEX idx_user_saved_recipes_user_id ON user_saved_recipes (user_id, saved_at DESC);

-- ---------------------------------------------------------------------------
-- menu_plans + menu_items + shopping_lists
-- ---------------------------------------------------------------------------
CREATE TABLE menu_plans (
    id         UUID        PRIMARY KEY DEFAULT uuid_generate_v7(),
    user_id    UUID        NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    week_start DATE        NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (user_id, week_start)
);

CREATE TABLE menu_items (
    id           UUID        PRIMARY KEY DEFAULT uuid_generate_v7(),
    menu_plan_id UUID        NOT NULL REFERENCES menu_plans(id) ON DELETE CASCADE,
    day_of_week  INT         NOT NULL CHECK (day_of_week BETWEEN 1 AND 7),
    meal_type    TEXT        NOT NULL CHECK (meal_type IN ('breakfast','lunch','dinner')),
    recipe_id    UUID        REFERENCES recipes(id) ON DELETE SET NULL,
    dish_id      UUID        REFERENCES dishes(id)  ON DELETE SET NULL,
    recipe_data  JSONB,
    UNIQUE (menu_plan_id, day_of_week, meal_type)
);

CREATE TABLE shopping_lists (
    id           UUID        PRIMARY KEY DEFAULT uuid_generate_v7(),
    user_id      UUID        NOT NULL REFERENCES users(id)       ON DELETE CASCADE,
    menu_plan_id UUID        REFERENCES menu_plans(id)           ON DELETE CASCADE,
    items        JSONB       NOT NULL DEFAULT '[]',
    generated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (user_id, menu_plan_id)
);

-- ---------------------------------------------------------------------------
-- meal_diary
-- ---------------------------------------------------------------------------
CREATE TABLE meal_diary (
    id         UUID          PRIMARY KEY DEFAULT uuid_generate_v7(),
    user_id    UUID          NOT NULL REFERENCES users(id)   ON DELETE CASCADE,
    date       DATE          NOT NULL,
    meal_type  TEXT          NOT NULL,
    name       TEXT          NOT NULL,
    portions   DECIMAL(5,2)  NOT NULL DEFAULT 1,
    calories   DECIMAL(8,2),
    protein_g  DECIMAL(8,2),
    fat_g      DECIMAL(8,2),
    carbs_g    DECIMAL(8,2),
    source     TEXT          NOT NULL DEFAULT 'manual',
    dish_id    UUID          REFERENCES dishes(id)   ON DELETE SET NULL,
    recipe_id  UUID          REFERENCES recipes(id)  ON DELETE SET NULL,
    portion_g  DECIMAL(10,2),
    created_at TIMESTAMPTZ   NOT NULL DEFAULT now()
);
CREATE INDEX idx_meal_diary_user_date ON meal_diary (user_id, date);

-- +goose Down
DROP TABLE IF EXISTS meal_diary;
DROP TABLE IF EXISTS shopping_lists;
DROP TABLE IF EXISTS menu_items;
DROP TABLE IF EXISTS menu_plans;
DROP TABLE IF EXISTS user_saved_recipes;
DROP TABLE IF EXISTS product_ingredients;
DROP TABLE IF EXISTS products;
DROP TABLE IF EXISTS recipe_step_translations;
DROP TABLE IF EXISTS recipe_steps;
DROP TABLE IF EXISTS recipe_ingredient_translations;
DROP TABLE IF EXISTS recipe_ingredients;
DROP TABLE IF EXISTS recipe_translations;
DROP TABLE IF EXISTS recipes;
DROP TABLE IF EXISTS dish_tags;
DROP TABLE IF EXISTS dish_translations;
DROP TABLE IF EXISTS dishes;
DROP TABLE IF EXISTS dish_category_translations;
DROP TABLE IF EXISTS dish_categories;
DROP TABLE IF EXISTS tag_translations;
DROP TABLE IF EXISTS tags;
DROP TABLE IF EXISTS cuisine_translations;
DROP TABLE IF EXISTS cuisines;
DROP TABLE IF EXISTS ingredient_aliases;
DROP TABLE IF EXISTS ingredient_translations;
DROP TABLE IF EXISTS ingredients;
DROP TABLE IF EXISTS ingredient_category_translations;
DROP TABLE IF EXISTS ingredient_categories;
DROP TABLE IF EXISTS unit_translations;
DROP TABLE IF EXISTS units;
DROP TABLE IF EXISTS languages;
DROP TABLE IF EXISTS users;
DROP TYPE IF EXISTS recipe_difficulty;
DROP TYPE IF EXISTS recipe_source;
DROP TYPE IF EXISTS activity_level;
DROP TYPE IF EXISTS user_goal;
DROP TYPE IF EXISTS user_gender;
DROP TYPE IF EXISTS user_plan;
DROP FUNCTION IF EXISTS uuid_generate_v7();
DROP EXTENSION IF EXISTS pg_trgm;
