-- CleanPlate Initial Schema
-- Migration 001: Core tables for MVP

BEGIN;

-- ============================================================
-- EXTENSIONS
-- ============================================================

CREATE EXTENSION IF NOT EXISTS "pgcrypto";

-- ============================================================
-- USERS & AUTH
-- ============================================================

CREATE TABLE users (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email                   VARCHAR(255) UNIQUE NOT NULL,
    password_hash           VARCHAR(255) NOT NULL,
    display_name            VARCHAR(100) NOT NULL,
    avatar_url              VARCHAR(500),
    subscription_tier       VARCHAR(20) DEFAULT 'free',
    subscription_expires_at TIMESTAMPTZ,
    created_at              TIMESTAMPTZ DEFAULT NOW(),
    updated_at              TIMESTAMPTZ DEFAULT NOW(),
    deleted_at              TIMESTAMPTZ
);

CREATE INDEX idx_users_email ON users(email) WHERE deleted_at IS NULL;

CREATE TABLE refresh_tokens (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id     UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    token_hash  VARCHAR(255) NOT NULL,
    expires_at  TIMESTAMPTZ NOT NULL,
    created_at  TIMESTAMPTZ DEFAULT NOW(),
    revoked_at  TIMESTAMPTZ
);

CREATE INDEX idx_refresh_tokens_user ON refresh_tokens(user_id) WHERE revoked_at IS NULL;
CREATE INDEX idx_refresh_tokens_hash ON refresh_tokens(token_hash) WHERE revoked_at IS NULL;

CREATE TABLE dietary_profiles (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id                 UUID UNIQUE NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    diet_type               VARCHAR(50),
    allergies               TEXT[] DEFAULT '{}',
    intolerances            TEXT[] DEFAULT '{}',
    calorie_target          INTEGER,
    excluded_ingredients    TEXT[] DEFAULT '{}',
    preferences_json        JSONB DEFAULT '{}',
    updated_at              TIMESTAMPTZ DEFAULT NOW()
);

-- ============================================================
-- HOUSEHOLDS
-- ============================================================

CREATE TABLE households (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name        VARCHAR(100) NOT NULL,
    owner_id    UUID NOT NULL REFERENCES users(id),
    created_at  TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE household_members (
    household_id UUID NOT NULL REFERENCES households(id) ON DELETE CASCADE,
    user_id      UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    role         VARCHAR(20) DEFAULT 'member',
    joined_at    TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (household_id, user_id)
);

-- ============================================================
-- CANONICAL INGREDIENTS (reference dictionary)
-- ============================================================

CREATE TABLE canonical_ingredients (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name                VARCHAR(200) UNIQUE NOT NULL,
    category            VARCHAR(50),
    aliases             TEXT[] DEFAULT '{}',
    nutrition_per_100g  JSONB,
    common_units        TEXT[] DEFAULT '{}'
);

CREATE INDEX idx_canonical_name ON canonical_ingredients(name);

-- ============================================================
-- RECIPES
-- ============================================================

CREATE TABLE recipes (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    author_id       UUID REFERENCES users(id),
    title           VARCHAR(300) NOT NULL,
    slug            VARCHAR(350) UNIQUE NOT NULL,
    description     TEXT,
    cuisine         VARCHAR(50),
    difficulty      VARCHAR(20),
    prep_time_min   INTEGER,
    cook_time_min   INTEGER,
    total_time_min  INTEGER GENERATED ALWAYS AS (
        COALESCE(prep_time_min, 0) + COALESCE(cook_time_min, 0)
    ) STORED,
    servings        INTEGER DEFAULT 4,
    source_type     VARCHAR(20) DEFAULT 'user',
    status          VARCHAR(20) DEFAULT 'published',
    cover_image_url VARCHAR(500),
    video_url       VARCHAR(500),
    tags            TEXT[] DEFAULT '{}',
    diet_labels     TEXT[] DEFAULT '{}',
    rating_avg      NUMERIC(3,2) DEFAULT 0,
    rating_count    INTEGER DEFAULT 0,
    save_count      INTEGER DEFAULT 0,
    version         INTEGER DEFAULT 1,
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW(),
    published_at    TIMESTAMPTZ
);

CREATE INDEX idx_recipes_cuisine ON recipes(cuisine);
CREATE INDEX idx_recipes_difficulty ON recipes(difficulty);
CREATE INDEX idx_recipes_total_time ON recipes(total_time_min);
CREATE INDEX idx_recipes_rating ON recipes(rating_avg DESC);
CREATE INDEX idx_recipes_tags ON recipes USING GIN(tags);
CREATE INDEX idx_recipes_diet_labels ON recipes USING GIN(diet_labels);
CREATE INDEX idx_recipes_status ON recipes(status) WHERE status = 'published';
CREATE INDEX idx_recipes_author ON recipes(author_id);

-- Full-text search vector
ALTER TABLE recipes ADD COLUMN search_vector tsvector
    GENERATED ALWAYS AS (
        setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
        setweight(to_tsvector('english', coalesce(description, '')), 'B') ||
        setweight(to_tsvector('english', coalesce(array_to_string(tags, ' '), '')), 'C')
    ) STORED;
CREATE INDEX idx_recipes_search ON recipes USING GIN(search_vector);

-- ============================================================
-- INGREDIENTS
-- ============================================================

CREATE TABLE ingredients (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    recipe_id               UUID NOT NULL REFERENCES recipes(id) ON DELETE CASCADE,
    name                    VARCHAR(200) NOT NULL,
    quantity                NUMERIC(10,3),
    unit                    VARCHAR(50),
    group_name              VARCHAR(100),
    sort_order              INTEGER NOT NULL,
    optional                BOOLEAN DEFAULT FALSE,
    canonical_ingredient_id UUID REFERENCES canonical_ingredients(id)
);

CREATE INDEX idx_ingredients_recipe ON ingredients(recipe_id);
CREATE INDEX idx_ingredients_canonical ON ingredients(canonical_ingredient_id);

-- ============================================================
-- RECIPE STEPS
-- ============================================================

CREATE TABLE recipe_steps (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    recipe_id   UUID NOT NULL REFERENCES recipes(id) ON DELETE CASCADE,
    step_number INTEGER NOT NULL,
    instruction TEXT NOT NULL,
    duration_min INTEGER,
    image_url   VARCHAR(500),
    tip         TEXT,
    UNIQUE(recipe_id, step_number)
);

CREATE INDEX idx_steps_recipe ON recipe_steps(recipe_id);

-- ============================================================
-- RECIPE NUTRITION
-- ============================================================

CREATE TABLE recipe_nutrition (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    recipe_id       UUID UNIQUE NOT NULL REFERENCES recipes(id) ON DELETE CASCADE,
    per_serving     BOOLEAN DEFAULT TRUE,
    calories        NUMERIC(8,2),
    protein_g       NUMERIC(8,2),
    fat_g           NUMERIC(8,2),
    saturated_fat_g NUMERIC(8,2),
    carbs_g         NUMERIC(8,2),
    fiber_g         NUMERIC(8,2),
    sugar_g         NUMERIC(8,2),
    sodium_mg       NUMERIC(8,2),
    calculated_at   TIMESTAMPTZ DEFAULT NOW(),
    source          VARCHAR(20) DEFAULT 'auto'
);

-- ============================================================
-- COMMUNITY (Reviews)
-- ============================================================

CREATE TABLE reviews (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    recipe_id       UUID NOT NULL REFERENCES recipes(id) ON DELETE CASCADE,
    user_id         UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    rating          SMALLINT NOT NULL CHECK (rating BETWEEN 1 AND 5),
    title           VARCHAR(200),
    body            TEXT,
    helpful_count   INTEGER DEFAULT 0,
    status          VARCHAR(20) DEFAULT 'published',
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(recipe_id, user_id)
);

CREATE INDEX idx_reviews_recipe ON reviews(recipe_id, created_at DESC);
CREATE INDEX idx_reviews_user ON reviews(user_id);

CREATE TABLE review_reports (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    review_id   UUID NOT NULL REFERENCES reviews(id) ON DELETE CASCADE,
    reporter_id UUID NOT NULL REFERENCES users(id),
    reason      VARCHAR(50) NOT NULL,
    details     TEXT,
    status      VARCHAR(20) DEFAULT 'pending',
    created_at  TIMESTAMPTZ DEFAULT NOW()
);

-- ============================================================
-- SAVED RECIPES & COLLECTIONS
-- ============================================================

CREATE TABLE saved_recipes (
    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 DEFAULT NOW(),
    synced_at   TIMESTAMPTZ,
    PRIMARY KEY (user_id, recipe_id)
);

CREATE TABLE collections (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id     UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    name        VARCHAR(100) NOT NULL,
    description TEXT,
    is_public   BOOLEAN DEFAULT FALSE,
    created_at  TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE collection_recipes (
    collection_id UUID NOT NULL REFERENCES collections(id) ON DELETE CASCADE,
    recipe_id     UUID NOT NULL REFERENCES recipes(id) ON DELETE CASCADE,
    added_at      TIMESTAMPTZ DEFAULT NOW(),
    sort_order    INTEGER,
    PRIMARY KEY (collection_id, recipe_id)
);

-- ============================================================
-- MEAL PLANNING
-- ============================================================

CREATE TABLE meal_plans (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id         UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    household_id    UUID REFERENCES households(id),
    name            VARCHAR(100),
    start_date      DATE NOT NULL,
    end_date        DATE NOT NULL,
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE meal_plan_items (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    plan_id     UUID NOT NULL REFERENCES meal_plans(id) ON DELETE CASCADE,
    recipe_id   UUID NOT NULL REFERENCES recipes(id),
    day_of_week SMALLINT NOT NULL CHECK (day_of_week BETWEEN 0 AND 6),
    meal_type   VARCHAR(20) NOT NULL,
    servings    INTEGER DEFAULT 1,
    notes       TEXT,
    sort_order  INTEGER
);

CREATE TABLE grocery_lists (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    plan_id     UUID NOT NULL REFERENCES meal_plans(id) ON DELETE CASCADE,
    generated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE grocery_items (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    list_id         UUID NOT NULL REFERENCES grocery_lists(id) ON DELETE CASCADE,
    ingredient_name VARCHAR(200) NOT NULL,
    quantity        NUMERIC(10,3),
    unit            VARCHAR(50),
    category        VARCHAR(50),
    checked         BOOLEAN DEFAULT FALSE,
    added_manually  BOOLEAN DEFAULT FALSE
);

-- ============================================================
-- MEDIA
-- ============================================================

CREATE TABLE media (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    uploader_id     UUID NOT NULL REFERENCES users(id),
    type            VARCHAR(20) NOT NULL,
    original_url    VARCHAR(500) NOT NULL,
    processed_url   VARCHAR(500),
    thumbnail_url   VARCHAR(500),
    status          VARCHAR(20) DEFAULT 'processing',
    mime_type       VARCHAR(100),
    size_bytes      BIGINT,
    duration_sec    INTEGER,
    width           INTEGER,
    height          INTEGER,
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

-- ============================================================
-- AI GENERATION LOG
-- ============================================================

CREATE TABLE ai_generations (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id     UUID NOT NULL REFERENCES users(id),
    prompt_hash VARCHAR(64) NOT NULL,
    input_json  JSONB NOT NULL,
    output_json JSONB,
    model_used  VARCHAR(100),
    tokens_in   INTEGER,
    tokens_out  INTEGER,
    latency_ms  INTEGER,
    cached      BOOLEAN DEFAULT FALSE,
    created_at  TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_ai_gen_prompt_hash ON ai_generations(prompt_hash);
CREATE INDEX idx_ai_gen_user ON ai_generations(user_id, created_at DESC);

-- ============================================================
-- SYNC TRACKING
-- ============================================================

CREATE TABLE sync_log (
    id          BIGSERIAL PRIMARY KEY,
    entity_type VARCHAR(50) NOT NULL,
    entity_id   UUID NOT NULL,
    action      VARCHAR(20) NOT NULL,
    changed_at  TIMESTAMPTZ DEFAULT NOW(),
    changed_by  UUID REFERENCES users(id)
);

CREATE INDEX idx_sync_log_type_time ON sync_log(entity_type, changed_at);

-- ============================================================
-- UPDATED_AT TRIGGER FUNCTION
-- ============================================================

CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ language 'plpgsql';

-- Apply trigger to tables with updated_at
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_recipes_updated_at BEFORE UPDATE ON recipes
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_reviews_updated_at BEFORE UPDATE ON reviews
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_meal_plans_updated_at BEFORE UPDATE ON meal_plans
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

COMMIT;
