-- 0002_products.sql — SD-0002 §6.3 data model (SLICE-5). Forward-only (INV-7):
-- never edit once merged; add a new numbered migration.

-- product — one catalog product per (storefront, handle) (INV-13/14). Option *names*
-- live here; product_type's kit values are schema-open but a service-layer rule
-- rejects non-'standalone' until #15 (same pattern as INV-4).
CREATE TABLE product (
    id            BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    storefront_id BIGINT NOT NULL REFERENCES storefront (id),
    handle        TEXT NOT NULL,
    title         TEXT NOT NULL,
    description_html TEXT,
    vendor        TEXT,
    product_type  TEXT NOT NULL DEFAULT 'standalone'
                  CHECK (product_type IN ('standalone', 'kit_virtual', 'kit_assembled')),
    google_product_category TEXT,
    tags          TEXT[] NOT NULL DEFAULT '{}',
    status        TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('draft', 'active', 'archived')),
    published     BOOLEAN NOT NULL DEFAULT TRUE,
    option1_name  TEXT,
    option2_name  TEXT,
    option3_name  TEXT,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX product_handle_key ON product (storefront_id, handle);  -- INV-13

-- variant — one purchasable form, identified by its option-value combo (INV-13).
-- SKU is indexed data, never identity. image_id FK is added after product_image.
CREATE TABLE variant (
    id            BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    product_id    BIGINT NOT NULL REFERENCES product (id),
    position      INTEGER NOT NULL,
    option1_value TEXT,
    option2_value TEXT,
    option3_value TEXT,
    sku           TEXT,
    barcode       TEXT,
    price         NUMERIC,
    cost          NUMERIC,
    weight        NUMERIC,
    weight_unit   TEXT,
    volume        NUMERIC,
    volume_unit   TEXT,
    tax_id_1      TEXT,
    tax_id_2      TEXT,
    inventory_tracker TEXT,
    inventory_qty INTEGER,
    image_id      BIGINT,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Postgres 16 (compose + Cloud SQL pin): NULLS NOT DISTINCT makes the all-NULL
-- no-option combo unique too (INV-13).
CREATE UNIQUE INDEX variant_option_combo_key
    ON variant (product_id, option1_value, option2_value, option3_value)
    NULLS NOT DISTINCT;
CREATE INDEX variant_sku_idx ON variant (sku);

-- product_image — identity within a product is source_url (§6.3); bytes live in
-- object storage from SLICE-7 (keys nullable until fetched). status starts 'pending';
-- SLICE-5 stubs the fetch phase so rows simply stay pending.
CREATE TABLE product_image (
    id            BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    product_id    BIGINT NOT NULL REFERENCES product (id),
    position      INTEGER NOT NULL,
    source_url    TEXT NOT NULL,
    alt_text      TEXT,
    status        TEXT NOT NULL DEFAULT 'pending'
                  CHECK (status IN ('pending', 'fetched', 'rejected_low_res', 'rejected_not_image', 'failed')),
    failure_reason TEXT,
    key_original  TEXT,
    key_thumb     TEXT,
    key_card      TEXT,
    key_detail    TEXT,
    import_run_id BIGINT,
    fetched_at    TIMESTAMPTZ,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX product_image_src_key ON product_image (product_id, source_url);
ALTER TABLE variant
    ADD CONSTRAINT variant_image_fk FOREIGN KEY (image_id) REFERENCES product_image (id);

-- import_draft — the preview's server side (INV-11). file_bytes is the SLICE-5
-- interim home for the upload (objectstore key from SLICE-7). Deleted outright on
-- cancel/expiry — drafts never appear in history (PUC-3a).
CREATE TABLE import_draft (
    id            BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    storefront_id BIGINT NOT NULL REFERENCES storefront (id),
    account_id    BIGINT NOT NULL REFERENCES account (id),
    file_name     TEXT NOT NULL,
    dialect       TEXT NOT NULL,
    file_bytes    BYTEA NOT NULL,
    summary       JSONB NOT NULL,
    records       JSONB NOT NULL,
    fingerprint   TEXT NOT NULL,
    unknown_columns TEXT[] NOT NULL DEFAULT '{}',
    expires_at    TIMESTAMPTZ NOT NULL,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- import_run — the durable record of one confirmed import (PUC-8); created only at
-- confirm (PUC-4).
CREATE TABLE import_run (
    id            BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    storefront_id BIGINT NOT NULL REFERENCES storefront (id),
    account_id    BIGINT NOT NULL REFERENCES account (id),
    file_name     TEXT NOT NULL,
    dialect       TEXT NOT NULL,
    products_added   INTEGER NOT NULL,
    products_updated INTEGER NOT NULL,
    rows_errored     INTEGER NOT NULL,
    status        TEXT NOT NULL
                  CHECK (status IN ('applying', 'fetching_images', 'complete', 'complete_with_problems')),
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    completed_at  TIMESTAMPTZ
);
CREATE INDEX import_run_history_idx ON import_run (storefront_id, created_at DESC);

-- import_run_error — one row per rejected CSV row (PUC-5), merchant-language message.
CREATE TABLE import_run_error (
    id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    run_id      BIGINT NOT NULL REFERENCES import_run (id),
    line_number INTEGER NOT NULL,
    column_name TEXT,
    message     TEXT NOT NULL
);

ALTER TABLE product_image
    ADD CONSTRAINT product_image_run_fk FOREIGN KEY (import_run_id) REFERENCES import_run (id);
