-- ============================================================================
-- Jetour BI Dashboard — Initial Schema
-- Creates all tables, triggers, indexes, and default settings
-- ============================================================================

BEGIN;

-- ── Auth ───────────────────────────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS dashboard_users (
    id          SERIAL PRIMARY KEY,
    username    VARCHAR(100) NOT NULL UNIQUE,
    email       VARCHAR(200),
    password_hash TEXT NOT NULL,
    role        VARCHAR(20) DEFAULT 'viewer',
    active      BOOLEAN DEFAULT true,
    created_at  TIMESTAMPTZ DEFAULT NOW(),
    last_login  TIMESTAMPTZ
);

CREATE TABLE IF NOT EXISTS password_reset_tokens (
    id          SERIAL PRIMARY KEY,
    user_id     INT NOT NULL REFERENCES dashboard_users(id) ON DELETE CASCADE,
    token       VARCHAR(200) NOT NULL UNIQUE,
    expires_at  TIMESTAMPTZ NOT NULL,
    used        BOOLEAN DEFAULT false,
    created_at  TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_reset_token ON password_reset_tokens(token) WHERE NOT used;

-- ── BI Data Tables (populated by CMS BI via n8n) ───────────────────────────

CREATE TABLE IF NOT EXISTS prospects (
    id              BIGSERIAL PRIMARY KEY,
    prospect_id     BIGINT NOT NULL,
    cms_prospect_id INTEGER,
    name            VARCHAR(100),
    surname         VARCHAR(100),
    id_number       VARCHAR(50),
    cell_number     VARCHAR(50),
    email_addr      VARCHAR(200),
    prospect_status VARCHAR(50),
    sales_person    VARCHAR(150),
    source_of_initial_contact VARCHAR(100),
    referral_source VARCHAR(150),
    lost_reason     TEXT,
    dt_prospect_created TIMESTAMPTZ,
    dt_updated      TIMESTAMPTZ,
    export_guid     UUID,
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_prospects_status ON prospects(prospect_status);
CREATE INDEX IF NOT EXISTS idx_prospects_created ON prospects(dt_prospect_created);
CREATE INDEX IF NOT EXISTS idx_prospects_sales ON prospects(sales_person);
CREATE UNIQUE INDEX IF NOT EXISTS idx_prospects_cms ON prospects(cms_prospect_id) WHERE cms_prospect_id IS NOT NULL;

CREATE TABLE IF NOT EXISTS prospect_otps (
    id              BIGSERIAL PRIMARY KEY,
    prospect_id     BIGINT REFERENCES prospects(prospect_id),
    cms_prospect_id INTEGER,
    mm_code         VARCHAR(50),
    make            VARCHAR(100),
    model           VARCHAR(200),
    model_range     VARCHAR(100),
    model_year      INTEGER,
    colour          VARCHAR(100),
    vin             VARCHAR(50),
    stock_no        VARCHAR(50),
    amount          NUMERIC(18,2),
    otp_new_used    VARCHAR(20),
    dt_otp_created  TIMESTAMPTZ,
    dt_sold         TIMESTAMPTZ,
    dt_invoiced     TIMESTAMPTZ,
    export_guid     UUID,
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_otps_prospect ON prospect_otps(prospect_id);
CREATE INDEX IF NOT EXISTS idx_otps_model ON prospect_otps(model);
CREATE INDEX IF NOT EXISTS idx_otps_range ON prospect_otps(model_range);
CREATE INDEX IF NOT EXISTS idx_otps_created ON prospect_otps(dt_otp_created);
CREATE INDEX IF NOT EXISTS idx_otps_invoiced ON prospect_otps(dt_invoiced);
CREATE INDEX IF NOT EXISTS idx_otps_stock ON prospect_otps(stock_no);

CREATE TABLE IF NOT EXISTS status_logs (
    id              BIGSERIAL PRIMARY KEY,
    prospect_id     BIGINT REFERENCES prospects(prospect_id),
    old_status      VARCHAR(50),
    new_status      VARCHAR(50),
    changed_by      VARCHAR(100),
    dt_changed      TIMESTAMPTZ DEFAULT NOW(),
    created_at      TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_slogs_prospect ON status_logs(prospect_id);
CREATE INDEX IF NOT EXISTS idx_slogs_date ON status_logs(dt_changed);

CREATE TABLE IF NOT EXISTS change_log (
    id              BIGSERIAL PRIMARY KEY,
    table_name      VARCHAR(100) NOT NULL,
    record_id       BIGINT NOT NULL,
    field_name      VARCHAR(100) NOT NULL,
    old_value       TEXT,
    new_value       TEXT,
    changed_at      TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_clog_table ON change_log(table_name, record_id);
CREATE INDEX IF NOT EXISTS idx_clog_date ON change_log(changed_at);

-- Change tracking trigger function
CREATE OR REPLACE FUNCTION track_changes() RETURNS TRIGGER AS $$
DECLARE
    col_name TEXT;
    old_val TEXT;
    new_val TEXT;
BEGIN
    FOR col_name IN
        SELECT column_name FROM information_schema.columns
        WHERE table_schema = TG_TABLE_SCHEMA AND table_name = TG_TABLE_NAME
          AND column_name NOT IN ('id', 'created_at', 'updated_at', 'export_guid')
    LOOP
        EXECUTE format('SELECT ($1).%I::text, ($2).%I::text', col_name, col_name)
        INTO old_val, new_val USING OLD, NEW;
        IF old_val IS DISTINCT FROM new_val THEN
            INSERT INTO change_log (table_name, record_id, field_name, old_value, new_value)
            VALUES (TG_TABLE_NAME, NEW.id, col_name, old_val, new_val);
        END IF;
    END LOOP;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS trg_prospects_changes ON prospects;
CREATE TRIGGER trg_prospects_changes AFTER UPDATE ON prospects
    FOR EACH ROW EXECUTE FUNCTION track_changes();

DROP TRIGGER IF EXISTS trg_otps_changes ON prospect_otps;
CREATE TRIGGER trg_otps_changes AFTER UPDATE ON prospect_otps
    FOR EACH ROW EXECUTE FUNCTION track_changes();

-- ── Appointments ───────────────────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS appointments (
    id                  BIGSERIAL PRIMARY KEY,
    prospect_id         BIGINT REFERENCES prospects(prospect_id),
    cms_prospect_id     INTEGER,
    dt_scheduled        TIMESTAMPTZ,
    appointment_type    VARCHAR(100),
    appointment_status  VARCHAR(50),
    confirmed           BOOLEAN,
    created_at          TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_appt_prospect ON appointments(prospect_id);
CREATE INDEX IF NOT EXISTS idx_appt_type ON appointments(appointment_type);

-- ── Leads ──────────────────────────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS leads (
    id              BIGSERIAL PRIMARY KEY,
    lead_id         INTEGER,
    prospect_id     BIGINT REFERENCES prospects(prospect_id),
    cms_prospect_id INTEGER,
    name            VARCHAR(100),
    surname         VARCHAR(100),
    cell_number     VARCHAR(50),
    email_addr      VARCHAR(200),
    prospect_status VARCHAR(50),
    lead_source     VARCHAR(50),
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

-- ── Report Snapshots (pre-computed aggregations) ───────────────────────────

CREATE TABLE IF NOT EXISTS report_daily_kpis (
    id              BIGSERIAL PRIMARY KEY,
    report_date     DATE NOT NULL UNIQUE,
    total_prospects INT DEFAULT 0,
    new_prospects   INT DEFAULT 0,
    total_otps      INT DEFAULT 0,
    new_otps        INT DEFAULT 0,
    sold_count      INT DEFAULT 0,
    pipeline_value  NUMERIC(18,2) DEFAULT 0,
    avg_otp_value   NUMERIC(18,2) DEFAULT 0,
    pending_count   INT DEFAULT 0,
    finance_count   INT DEFAULT 0,
    lost_count      INT DEFAULT 0,
    conversion_rate NUMERIC(5,2) DEFAULT 0,
    generated_at    TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS report_model_performance (
    id              BIGSERIAL PRIMARY KEY,
    report_date     DATE NOT NULL,
    model_group     VARCHAR(100) NOT NULL,
    model           VARCHAR(200) NOT NULL,
    color           VARCHAR(100),
    otp_type        VARCHAR(20),
    otp_count       INT DEFAULT 0,
    total_amount    NUMERIC(18,2) DEFAULT 0,
    avg_amount      NUMERIC(18,2) DEFAULT 0,
    sold_count      INT DEFAULT 0,
    generated_at    TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(report_date, model_group, model, color, otp_type)
);

CREATE TABLE IF NOT EXISTS report_salesperson_performance (
    id                  BIGSERIAL PRIMARY KEY,
    report_date         DATE NOT NULL,
    sales_person        VARCHAR(150) NOT NULL,
    total_prospects     INT DEFAULT 0,
    otp_count           INT DEFAULT 0,
    sold_count          INT DEFAULT 0,
    finance_count       INT DEFAULT 0,
    lost_count          INT DEFAULT 0,
    total_pipeline_value NUMERIC(18,2) DEFAULT 0,
    generated_at        TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(report_date, sales_person)
);

-- ── App Settings & Recipients ──────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS report_recipients (
    id          SERIAL PRIMARY KEY,
    report_type VARCHAR(20) NOT NULL,
    email       VARCHAR(200) NOT NULL,
    name        VARCHAR(100),
    active      BOOLEAN DEFAULT true,
    added_at    TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(report_type, email)
);

CREATE TABLE IF NOT EXISTS app_settings (
    key         VARCHAR(100) PRIMARY KEY,
    value       TEXT,
    updated_at  TIMESTAMPTZ DEFAULT NOW()
);

-- ── Default settings ───────────────────────────────────────────────────────

INSERT INTO app_settings (key, value) VALUES
    ('smtp_port', '587'),
    ('smtp_use_tls', 'true')
ON CONFLICT (key) DO NOTHING;

COMMIT;
