-- Migration: 000001_initial_schema
-- Description: Create initial Extrudex schema — lookup tables, core entities, and settings
-- Author: Hex
-- Date: 2026-05-06
--
-- Design decisions:
--   - Lookup tables for material_base, material_finish, material_modifier (no free-text enums)
--   - Lookup tables for printer_type and job_status (extensible, no hard-coded enum values)
--   - FK ON DELETE: RESTRICT on critical parents (material_base, material_finish, printer),
--     SET NULL on optional parents (modifier, spool on print_jobs),
--     CASCADE for usage_logs when parent job is deleted
--   - Soft-delete (deleted_at) on spools and print_jobs for safety
--   - JSONB config column on settings for flexible app-wide configuration
--   - All identifiers snake_case per project convention

BEGIN;

-- ============================================================================
-- Lookup Tables
-- ============================================================================

-- Printer types (fdm, resin, etc.) — extensible, not a raw enum
CREATE TABLE printer_types (
    id          SERIAL PRIMARY KEY,
    name        VARCHAR(50) NOT NULL UNIQUE,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Job statuses (pending, printing, paused, completed, failed, cancelled)
CREATE TABLE job_statuses (
    id          SERIAL PRIMARY KEY,
    name        VARCHAR(50) NOT NULL UNIQUE,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Material base types (PLA, PETG, ABS, TPU, ASA, Nylon, PC)
CREATE TABLE material_bases (
    id                  SERIAL PRIMARY KEY,
    name                VARCHAR(100) NOT NULL UNIQUE,
    density_g_cm3       DECIMAL(5,3) NOT NULL,
    extrusion_temp_min  INT,
    extrusion_temp_max  INT,
    bed_temp_min        INT,
    bed_temp_max        INT,
    created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Material finishes (Basic, Silk, Matte, Glossy, Satin)
CREATE TABLE material_finishes (
    id          SERIAL PRIMARY KEY,
    name        VARCHAR(100) NOT NULL UNIQUE,
    description TEXT,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Material modifiers (Wood-Filled, Carbon Fiber, Glow-in-Dark, Marble)
CREATE TABLE material_modifiers (
    id          SERIAL PRIMARY KEY,
    name        VARCHAR(100) NOT NULL UNIQUE,
    description TEXT,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================================================
-- Core Entity Tables
-- ============================================================================

-- 3D printers in the fleet
CREATE TABLE printers (
    id                  SERIAL PRIMARY KEY,
    name                VARCHAR(255) NOT NULL,
    printer_type_id     INT NOT NULL,
    manufacturer        VARCHAR(255),
    model               VARCHAR(255),
    moonraker_url       VARCHAR(512),
    moonraker_api_key   VARCHAR(512),
    mqtt_broker_host    VARCHAR(255),
    mqtt_topic_prefix   VARCHAR(255),
    mqtt_tls_enabled    BOOLEAN NOT NULL DEFAULT FALSE,
    is_active           BOOLEAN NOT NULL DEFAULT TRUE,
    created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    CONSTRAINT fk_printers_printer_type
        FOREIGN KEY (printer_type_id) REFERENCES printer_types(id)
        ON DELETE RESTRICT
);

-- Filament spools — the core inventory item
CREATE TABLE filament_spools (
    id                      SERIAL PRIMARY KEY,
    name                    VARCHAR(255) NOT NULL,
    material_base_id        INT NOT NULL,
    material_finish_id      INT NOT NULL DEFAULT 1,  -- "Basic" (seed data populates this first)
    material_modifier_id    INT,
    color_hex               VARCHAR(7) NOT NULL CHECK (color_hex ~ '^#[0-9A-Fa-f]{6}$'),
    brand                   VARCHAR(255),
    diameter_mm             DECIMAL(4,2) NOT NULL DEFAULT 1.75,
    initial_grams           INT NOT NULL CHECK (initial_grams > 0),
    remaining_grams         INT NOT NULL CHECK (remaining_grams >= 0),
    spool_weight_grams      INT,  -- measured empty-spool weight (tare), nullable
    cost_usd                DECIMAL(10,2),
    low_stock_threshold_grams INT NOT NULL DEFAULT 50,
    notes                   TEXT,
    barcode                 VARCHAR(255) UNIQUE,
    deleted_at              TIMESTAMPTZ,
    created_at              TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at              TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    CONSTRAINT fk_spools_material_base
        FOREIGN KEY (material_base_id) REFERENCES material_bases(id)
        ON DELETE RESTRICT,

    CONSTRAINT fk_spools_material_finish
        FOREIGN KEY (material_finish_id) REFERENCES material_finishes(id)
        ON DELETE RESTRICT,

    CONSTRAINT fk_spools_material_modifier
        FOREIGN KEY (material_modifier_id) REFERENCES material_modifiers(id)
        ON DELETE SET NULL
);

-- Print jobs — each job is one print on one printer
CREATE TABLE print_jobs (
    id                      SERIAL PRIMARY KEY,
    printer_id              INT NOT NULL,
    filament_spool_id       INT,  -- nullable: a job may use multiple spools (captured in usage_logs)
    job_name                VARCHAR(255) NOT NULL,
    file_name               VARCHAR(512),
    job_status_id           INT NOT NULL DEFAULT 1,  -- "pending"
    started_at              TIMESTAMPTZ,
    completed_at            TIMESTAMPTZ,
    duration_seconds        INT,
    estimated_duration_seconds INT,
    total_mm_extruded       DECIMAL(12,2),
    total_grams_used        DECIMAL(10,2),
    total_cost_usd          DECIMAL(10,4),
    notes                   TEXT,
    deleted_at              TIMESTAMPTZ,
    created_at              TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at              TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    CONSTRAINT fk_print_jobs_printer
        FOREIGN KEY (printer_id) REFERENCES printers(id)
        ON DELETE RESTRICT,

    CONSTRAINT fk_print_jobs_spool
        FOREIGN KEY (filament_spool_id) REFERENCES filament_spools(id)
        ON DELETE SET NULL,

    CONSTRAINT fk_print_jobs_status
        FOREIGN KEY (job_status_id) REFERENCES job_statuses(id)
        ON DELETE RESTRICT
);

-- Usage logs — granular tracking of filament consumed per job, per spool
CREATE TABLE usage_logs (
    id                  SERIAL PRIMARY KEY,
    print_job_id        INT NOT NULL,
    filament_spool_id   INT NOT NULL,
    mm_extruded         DECIMAL(12,2) NOT NULL CHECK (mm_extruded > 0),
    grams_used          DECIMAL(10,2) NOT NULL CHECK (grams_used > 0),
    cost_usd            DECIMAL(10,4),
    logged_at           TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    CONSTRAINT fk_usage_logs_print_job
        FOREIGN KEY (print_job_id) REFERENCES print_jobs(id)
        ON DELETE CASCADE,

    CONSTRAINT fk_usage_logs_spool
        FOREIGN KEY (filament_spool_id) REFERENCES filament_spools(id)
        ON DELETE RESTRICT
);

-- ============================================================================
-- Application Settings
-- ============================================================================

CREATE TABLE settings (
    id          SERIAL PRIMARY KEY,
    key         VARCHAR(255) NOT NULL UNIQUE,
    value       JSONB NOT NULL,
    description TEXT,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================================================
-- Indexes
-- ============================================================================

-- Filament spools — query patterns: lookup by material, low-stock scans, barcode scans
CREATE INDEX ix_spools_material_base_id     ON filament_spools(material_base_id);
CREATE INDEX ix_spools_material_finish_id   ON filament_spools(material_finish_id);
CREATE INDEX ix_spools_material_modifier_id ON filament_spools(material_modifier_id);
CREATE INDEX ix_spools_remaining_grams      ON filament_spools(remaining_grams)
    WHERE deleted_at IS NULL;  -- partial index: only active spools for low-stock queries
CREATE INDEX ix_spools_barcode              ON filament_spools(barcode)
    WHERE barcode IS NOT NULL AND deleted_at IS NULL;
CREATE INDEX ix_spools_deleted_at           ON filament_spools(deleted_at)
    WHERE deleted_at IS NOT NULL;  -- small index for soft-delete filtering

-- Printers
CREATE INDEX ix_printers_printer_type_id ON printers(printer_type_id);
CREATE INDEX ix_printers_is_active       ON printers(is_active)
    WHERE is_active = TRUE;  -- partial index for fleet dashboard queries

-- Print jobs — query by printer, status, date range, and soft-delete filter
CREATE INDEX ix_print_jobs_printer_id   ON print_jobs(printer_id);
CREATE INDEX ix_print_jobs_spool_id     ON print_jobs(filament_spool_id)
    WHERE filament_spool_id IS NOT NULL;
CREATE INDEX ix_print_jobs_status_id    ON print_jobs(job_status_id);
CREATE INDEX ix_print_jobs_created_at   ON print_jobs(created_at DESC);
CREATE INDEX ix_print_jobs_deleted_at   ON print_jobs(deleted_at)
    WHERE deleted_at IS NOT NULL;

-- Usage logs — always queried by job or spool
CREATE INDEX ix_usage_logs_print_job_id ON usage_logs(print_job_id);
CREATE INDEX ix_usage_logs_spool_id     ON usage_logs(filament_spool_id);
CREATE INDEX ix_usage_logs_logged_at    ON usage_logs(logged_at DESC);

-- Settings — key lookups
CREATE INDEX ix_settings_key ON settings(key);

COMMIT;
