-- 0001_init.sql — pansy initial schema.
--
-- Conventions (see DESIGN.md § Domain model):
--   * All measurements are centimeters (REAL); imperial is a display concern.
--   * `version INTEGER NOT NULL DEFAULT 1` on every user-mutable row; PATCH/DELETE
--     carry it and the service layer increments on write for 409 conflict detection.
--   * Enumerations are enforced with CHECK constraints.
--   * Timestamps are ISO-8601 TEXT (UTC); dates are 'YYYY-MM-DD' TEXT.
--   * Foreign keys cascade on owner/parent deletion; enforcement is on (PRAGMA foreign_keys).

-- users -----------------------------------------------------------------------
-- A user may authenticate locally (password_hash), via OIDC (oidc_issuer+subject),
-- or both (linked by email on first OIDC login). First registered user is admin.
CREATE TABLE users (
    id            INTEGER PRIMARY KEY,
    email         TEXT    NOT NULL UNIQUE COLLATE NOCASE,
    display_name  TEXT    NOT NULL,
    password_hash TEXT,                       -- argon2id; NULL for OIDC-only users
    oidc_issuer   TEXT,                       -- NULL for local-only users
    oidc_subject  TEXT,
    is_admin      INTEGER NOT NULL DEFAULT 0 CHECK (is_admin IN (0, 1)),
    version       INTEGER NOT NULL DEFAULT 1,
    created_at    TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
    updated_at    TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
-- NULLs compare distinct in SQLite, so this only constrains real (issuer, subject) pairs.
CREATE UNIQUE INDEX idx_users_oidc ON users (oidc_issuer, oidc_subject);

-- sessions --------------------------------------------------------------------
-- Opaque bearer token stored only as a sha256 hash; the raw token lives in the
-- HttpOnly cookie. 30-day sliding expiry (expires_at bumped on use).
CREATE TABLE sessions (
    token_hash TEXT    PRIMARY KEY,           -- sha256 of the 32-byte random token
    user_id    INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
    expires_at TEXT    NOT NULL,
    created_at TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
CREATE INDEX idx_sessions_user ON sessions (user_id);

-- gardens ---------------------------------------------------------------------
CREATE TABLE gardens (
    id         INTEGER PRIMARY KEY,
    owner_id   INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
    name       TEXT    NOT NULL,
    width_cm   REAL    NOT NULL,
    height_cm  REAL    NOT NULL,
    unit_pref  TEXT    NOT NULL DEFAULT 'metric' CHECK (unit_pref IN ('metric', 'imperial')),
    notes      TEXT    NOT NULL DEFAULT '',
    version    INTEGER NOT NULL DEFAULT 1,
    created_at TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
    updated_at TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
CREATE INDEX idx_gardens_owner ON gardens (owner_id);

-- garden_shares ---------------------------------------------------------------
-- The owner is implicit via gardens.owner_id and never has a share row.
CREATE TABLE garden_shares (
    id         INTEGER PRIMARY KEY,
    garden_id  INTEGER NOT NULL REFERENCES gardens (id) ON DELETE CASCADE,
    user_id    INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
    role       TEXT    NOT NULL CHECK (role IN ('viewer', 'editor')),
    created_by INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
    version    INTEGER NOT NULL DEFAULT 1,
    created_at TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
    updated_at TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
    UNIQUE (garden_id, user_id)
);
CREATE INDEX idx_garden_shares_garden ON garden_shares (garden_id);
CREATE INDEX idx_garden_shares_user ON garden_shares (user_id);

-- garden_objects --------------------------------------------------------------
-- One polymorphic table for every placeable object. Positioned by CENTER point +
-- rotation about center, in garden space (origin top-left, x→right, y→down).
-- shape='polygon' and the points JSON column are reserved for post-v1; the v1
-- editor emits only 'rect' and 'circle' (circle: width_cm = diameter).
CREATE TABLE garden_objects (
    id          INTEGER PRIMARY KEY,
    garden_id   INTEGER NOT NULL REFERENCES gardens (id) ON DELETE CASCADE,
    kind        TEXT    NOT NULL CHECK (kind IN ('bed', 'grow_bag', 'container', 'in_ground', 'tree', 'path', 'structure')),
    name        TEXT    NOT NULL DEFAULT '',
    shape       TEXT    NOT NULL DEFAULT 'rect' CHECK (shape IN ('rect', 'circle', 'polygon')),
    points      TEXT,                          -- reserved: JSON [[x,y],...] for shape='polygon'
    x_cm        REAL    NOT NULL,              -- center in garden space
    y_cm        REAL    NOT NULL,
    width_cm    REAL    NOT NULL,
    height_cm   REAL    NOT NULL,
    rotation_deg REAL   NOT NULL DEFAULT 0,    -- clockwise about center
    z_index     INTEGER NOT NULL DEFAULT 0,
    plantable   INTEGER NOT NULL DEFAULT 0 CHECK (plantable IN (0, 1)),
    color       TEXT,                          -- optional hex override
    props       TEXT,                          -- kind-specific extras (JSON)
    notes       TEXT    NOT NULL DEFAULT '',
    version     INTEGER NOT NULL DEFAULT 1,
    created_at  TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
    updated_at  TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
CREATE INDEX idx_garden_objects_garden ON garden_objects (garden_id);

-- plants ----------------------------------------------------------------------
-- Catalog. owner_id NULL = built-in (seeded, read-only; clone to customize).
-- Users see built-ins plus their own.
CREATE TABLE plants (
    id               INTEGER PRIMARY KEY,
    owner_id         INTEGER REFERENCES users (id) ON DELETE CASCADE,  -- NULL = built-in
    name             TEXT    NOT NULL,
    category         TEXT    NOT NULL CHECK (category IN ('vegetable', 'herb', 'flower', 'fruit', 'tree_shrub', 'cover')),
    spacing_cm       REAL    NOT NULL,          -- mature spacing
    color            TEXT    NOT NULL,          -- hex
    icon             TEXT    NOT NULL DEFAULT '', -- emoji (zero image assets in v1)
    days_to_maturity INTEGER,
    notes            TEXT    NOT NULL DEFAULT '',
    version          INTEGER NOT NULL DEFAULT 1,
    created_at       TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
    updated_at       TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
CREATE INDEX idx_plants_owner ON plants (owner_id);

-- plantings ("plops") ---------------------------------------------------------
-- A circular patch of one plant, positioned in the PARENT OBJECT's local frame
-- (origin at object center, unrotated axes) so moving/rotating the bed moves its
-- plants for free. count NULL = derived max(1, round(pi*r^2 / spacing_cm^2)).
-- "Clear bed" sets removed_at; v1 lists rows where removed_at IS NULL.
CREATE TABLE plantings (
    id         INTEGER PRIMARY KEY,
    object_id  INTEGER NOT NULL REFERENCES garden_objects (id) ON DELETE CASCADE,
    plant_id   INTEGER NOT NULL REFERENCES plants (id) ON DELETE RESTRICT,
    x_cm       REAL    NOT NULL,               -- center in object-local frame
    y_cm       REAL    NOT NULL,
    radius_cm  REAL    NOT NULL,
    count      INTEGER,                         -- NULL = derived from area / spacing^2
    label      TEXT,
    planted_at TEXT,                            -- 'YYYY-MM-DD'
    removed_at TEXT,                            -- 'YYYY-MM-DD'; NULL = currently planted
    version    INTEGER NOT NULL DEFAULT 1,
    created_at TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
    updated_at TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
CREATE INDEX idx_plantings_object ON plantings (object_id);
CREATE INDEX idx_plantings_plant ON plantings (plant_id);
