-- Flight Radar Web App - Database Schema
-- Version: 2.0
-- Date: 2026-02-23
-- Database: SQLite 3
--
-- This schema extends the existing cache.db with new tables for the web app.
-- Existing tables (flight_searches, flight_results) are preserved.

-- ============================================================================
-- CRITICAL: Enable Foreign Keys (SQLite default is OFF!)
-- ============================================================================
PRAGMA foreign_keys = ON;

-- ============================================================================
-- Table: scans
-- Purpose: Track flight scan requests and their status
-- ============================================================================
CREATE TABLE IF NOT EXISTS scans (
    -- Primary key with auto-increment
    id INTEGER PRIMARY KEY AUTOINCREMENT,

    -- Search parameters (validated by CHECK constraints)
    origin TEXT NOT NULL CHECK(length(origin) = 3),
    country TEXT NOT NULL CHECK(length(country) >= 2),
    start_date TEXT NOT NULL,  -- ISO 8601: YYYY-MM-DD
    end_date TEXT NOT NULL,

    -- Timestamps (auto-managed)
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

    -- Scan status (enforced enum via CHECK)
    status TEXT NOT NULL DEFAULT 'pending'
        CHECK(status IN ('pending', 'running', 'completed', 'failed')),

    -- Progress tracking
    total_routes INTEGER NOT NULL DEFAULT 0 CHECK(total_routes >= 0),
    routes_scanned INTEGER NOT NULL DEFAULT 0 CHECK(routes_scanned >= 0),
    total_flights INTEGER NOT NULL DEFAULT 0 CHECK(total_flights >= 0),

    -- Error information (NULL if no error)
    error_message TEXT,

    -- Additional search parameters
    seat_class TEXT DEFAULT 'economy',
    adults INTEGER DEFAULT 1 CHECK(adults > 0 AND adults <= 9),

    -- Constraints across columns
    CHECK(end_date >= start_date),
    CHECK(routes_scanned <= total_routes OR total_routes = 0)
);

-- Performance indexes for scans table
CREATE INDEX IF NOT EXISTS idx_scans_origin_country
    ON scans(origin, country);

CREATE INDEX IF NOT EXISTS idx_scans_status
    ON scans(status)
    WHERE status IN ('pending', 'running');  -- Partial index for active scans

CREATE INDEX IF NOT EXISTS idx_scans_created_at
    ON scans(created_at DESC);  -- For recent scans query

-- ============================================================================
-- Table: routes
-- Purpose: Store discovered routes with flight statistics
-- ============================================================================
CREATE TABLE IF NOT EXISTS routes (
    -- Primary key
    id INTEGER PRIMARY KEY AUTOINCREMENT,

    -- Foreign key to scans (cascade delete)
    scan_id INTEGER NOT NULL,

    -- Destination airport
    destination TEXT NOT NULL CHECK(length(destination) = 3),
    destination_name TEXT NOT NULL,
    destination_city TEXT,

    -- Flight statistics
    flight_count INTEGER NOT NULL DEFAULT 0 CHECK(flight_count >= 0),
    airlines TEXT NOT NULL,  -- JSON array: ["Ryanair", "Lufthansa"]

    -- Price statistics (NULL if no flights)
    min_price REAL CHECK(min_price >= 0),
    max_price REAL CHECK(max_price >= 0),
    avg_price REAL CHECK(avg_price >= 0),

    -- Timestamp
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

    -- Foreign key constraint with cascade delete
    FOREIGN KEY (scan_id)
        REFERENCES scans(id)
        ON DELETE CASCADE,

    -- Price consistency constraints
    CHECK(max_price >= min_price OR max_price IS NULL),
    CHECK(avg_price >= min_price OR avg_price IS NULL),
    CHECK(avg_price <= max_price OR avg_price IS NULL)
);

-- Performance indexes for routes table
CREATE INDEX IF NOT EXISTS idx_routes_scan_id
    ON routes(scan_id);

CREATE INDEX IF NOT EXISTS idx_routes_destination
    ON routes(destination);

CREATE INDEX IF NOT EXISTS idx_routes_min_price
    ON routes(min_price)
    WHERE min_price IS NOT NULL;  -- Partial index for routes with prices

-- One route row per (scan, destination) — enables incremental upsert writes
CREATE UNIQUE INDEX IF NOT EXISTS uq_routes_scan_dest
    ON routes(scan_id, destination);

-- ============================================================================
-- Triggers: Auto-update timestamps and aggregates
-- ============================================================================

-- Trigger: Update scans.updated_at on any update
CREATE TRIGGER IF NOT EXISTS update_scans_timestamp
AFTER UPDATE ON scans
FOR EACH ROW
BEGIN
    UPDATE scans
    SET updated_at = CURRENT_TIMESTAMP
    WHERE id = NEW.id;
END;

-- Trigger: Update total_flights count when routes are inserted
CREATE TRIGGER IF NOT EXISTS update_scan_flight_count_insert
AFTER INSERT ON routes
FOR EACH ROW
BEGIN
    UPDATE scans
    SET total_flights = (
        SELECT COALESCE(SUM(flight_count), 0)
        FROM routes
        WHERE scan_id = NEW.scan_id
    )
    WHERE id = NEW.scan_id;
END;

-- Trigger: Update total_flights count when routes are updated
CREATE TRIGGER IF NOT EXISTS update_scan_flight_count_update
AFTER UPDATE OF flight_count ON routes
FOR EACH ROW
BEGIN
    UPDATE scans
    SET total_flights = (
        SELECT COALESCE(SUM(flight_count), 0)
        FROM routes
        WHERE scan_id = NEW.scan_id
    )
    WHERE id = NEW.scan_id;
END;

-- Trigger: Update total_flights count when routes are deleted
CREATE TRIGGER IF NOT EXISTS update_scan_flight_count_delete
AFTER DELETE ON routes
FOR EACH ROW
BEGIN
    UPDATE scans
    SET total_flights = (
        SELECT COALESCE(SUM(flight_count), 0)
        FROM routes
        WHERE scan_id = OLD.scan_id
    )
    WHERE id = OLD.scan_id;
END;

-- ============================================================================
-- Table: flights
-- Purpose: Store individual flights discovered per scan
-- ============================================================================
CREATE TABLE IF NOT EXISTS flights (
    id INTEGER PRIMARY KEY AUTOINCREMENT,

    -- Foreign key to scans (cascade delete)
    scan_id INTEGER NOT NULL,

    -- Route
    destination TEXT NOT NULL CHECK(length(destination) = 3),
    date TEXT NOT NULL,  -- ISO 8601: YYYY-MM-DD

    -- Flight details
    airline TEXT,
    departure_time TEXT,   -- HH:MM
    arrival_time TEXT,     -- HH:MM
    price REAL CHECK(price >= 0),
    stops INTEGER NOT NULL DEFAULT 0,

    -- Timestamp
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

    FOREIGN KEY (scan_id)
        REFERENCES scans(id)
        ON DELETE CASCADE
);

CREATE INDEX IF NOT EXISTS idx_flights_scan_id
    ON flights(scan_id);

CREATE INDEX IF NOT EXISTS idx_flights_scan_dest
    ON flights(scan_id, destination);

CREATE INDEX IF NOT EXISTS idx_flights_price
    ON flights(scan_id, price ASC)
    WHERE price IS NOT NULL;

-- ============================================================================
-- Views: Useful queries
-- ============================================================================

-- View: Recent scans with route counts
CREATE VIEW IF NOT EXISTS recent_scans AS
SELECT
    s.id,
    s.origin,
    s.country,
    s.status,
    s.created_at,
    s.total_routes,
    s.total_flights,
    COUNT(r.id) as routes_found,
    MIN(r.min_price) as cheapest_flight,
    s.error_message
FROM scans s
LEFT JOIN routes r ON r.scan_id = s.id
GROUP BY s.id
ORDER BY s.created_at DESC
LIMIT 10;

-- View: Active scans (pending or running)
CREATE VIEW IF NOT EXISTS active_scans AS
SELECT *
FROM scans
WHERE status IN ('pending', 'running')
ORDER BY created_at ASC;

-- ============================================================================
-- Initial Data: None (tables start empty)
-- ============================================================================

-- Schema version tracking (for future migrations)
CREATE TABLE IF NOT EXISTS schema_version (
    version INTEGER PRIMARY KEY,
    applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    description TEXT
);

INSERT OR IGNORE INTO schema_version (version, description)
VALUES (1, 'Initial web app schema with scans and routes tables');

-- ============================================================================
-- Verification Queries (for testing)
-- ============================================================================

-- Uncomment to verify schema creation:
-- SELECT name, type FROM sqlite_master WHERE type IN ('table', 'index', 'trigger', 'view') ORDER BY type, name;
-- PRAGMA foreign_keys;
-- PRAGMA table_info(scans);
-- PRAGMA table_info(routes);
