-- Enable necessary extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE EXTENSION IF NOT EXISTS "pg_stat_statements";
CREATE EXTENSION IF NOT EXISTS "pg_trgm";

------ ENUMS ------
-- Core enums for status and types
CREATE TYPE scan_status AS ENUM (
    'pending',
    'queued',
    'running',
    'completed',
    'failed',
    'cancelled'
);

CREATE TYPE severity_level AS ENUM (
    'critical',
    'high',
    'medium',
    'low',
    'info'
);

CREATE TYPE comparison_operator AS ENUM (
    'less_than',
    'less_than_equal',
    'greater_than',
    'greater_than_equal',
    'equal_to',
    'not_equal_to'
);

CREATE TYPE metric_category AS ENUM (
    'performance',
    'seo',
    'accessibility',
    'best_practices',
    'security',
    'pwa'
);

CREATE TYPE resource_type AS ENUM (
    'script',
    'stylesheet',
    'image',
    'font',
    'document',
    'media',
    'other'
);

CREATE TYPE notification_channel AS ENUM (
    'email',
    'slack',
    'webhook',
    'in_app'
);

CREATE TYPE subscription_tier AS ENUM (
    'free',
    'starter',
    'professional',
    'enterprise'
);

CREATE TYPE user_role AS ENUM (
    'owner',
    'admin',
    'editor',
    'viewer'
);

------ CORE TABLES ------
-- Organizations table
CREATE TABLE organizations (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    name VARCHAR NOT NULL,
    subscription_tier subscription_tier DEFAULT 'free',
    subscription_status VARCHAR DEFAULT 'active',
    billing_email VARCHAR,
    max_websites INTEGER DEFAULT 5,
    max_users INTEGER DEFAULT 3,
    scan_frequency_minutes INTEGER DEFAULT 60,
    settings JSONB DEFAULT '{
        "alert_email_digest": "daily",
        "default_scan_depth": 3,
        "retention_days": 90,
        "enable_competitor_analysis": false
    }'::jsonb,
    metadata JSONB DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Users table
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    email VARCHAR UNIQUE NOT NULL,
    name VARCHAR,
    organization_id UUID REFERENCES organizations(id),
    role user_role DEFAULT 'viewer',
    is_active BOOLEAN DEFAULT true,
    last_login_at TIMESTAMPTZ,
    settings JSONB DEFAULT '{
        "email_notifications": true,
        "notification_frequency": "instant",
        "dashboard_layout": "default"
    }'::jsonb,
    metadata JSONB DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Websites table
CREATE TABLE websites (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    organization_id UUID REFERENCES organizations(id) NOT NULL,
    base_url VARCHAR NOT NULL,
    name VARCHAR NOT NULL,
    is_active BOOLEAN DEFAULT true,
    crawl_settings JSONB DEFAULT '{
        "max_pages": 100,
        "max_depth": 3,
        "exclude_patterns": [
            "/admin/*",
            "/api/*",
            "*.pdf",
            "*.jpg",
            "*.png"
        ],
        "include_patterns": ["/*"],
        "respect_robots_txt": true,
        "crawl_frequency": "daily",
        "crawl_timing": "off_peak"
    }'::jsonb,
    scan_schedule JSONB DEFAULT '{
        "frequency": "hourly",
        "time_windows": ["0-6", "20-23"],
        "days": ["monday", "tuesday", "wednesday", "thursday", "friday"]
    }'::jsonb,
    performance_budgets JSONB DEFAULT '{
        "page_weight_kb": 1000,
        "max_requests": 100,
        "time_to_interactive_ms": 3000,
        "first_contentful_paint_ms": 1000
    }'::jsonb,
    notifications JSONB DEFAULT '{
        "channels": ["email"],
        "thresholds": {
            "performance": 90,
            "accessibility": 90,
            "seo": 90,
            "best_practices": 90
        }
    }'::jsonb,
    last_crawl_at TIMESTAMPTZ,
    last_scan_at TIMESTAMPTZ,
    metadata JSONB DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(organization_id, base_url)
);

-- Competitor tracking
CREATE TABLE competitor_websites (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    website_id UUID REFERENCES websites(id) NOT NULL,
    competitor_url VARCHAR NOT NULL,
    name VARCHAR NOT NULL,
    is_active BOOLEAN DEFAULT true,
    scan_frequency VARCHAR DEFAULT 'daily',
    metrics_to_track VARCHAR[] DEFAULT ARRAY[
        'performance',
        'seo',
        'accessibility',
        'best_practices'
    ],
    last_scan_at TIMESTAMPTZ,
    metadata JSONB DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(website_id, competitor_url)
);

-- Pages table
CREATE TABLE pages (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    website_id UUID REFERENCES websites(id) NOT NULL,
    url VARCHAR NOT NULL,
    path VARCHAR NOT NULL,
    title VARCHAR,
    description TEXT,
    content_hash VARCHAR,
    content_type VARCHAR,
    status_code INTEGER,
    is_active BOOLEAN DEFAULT true,
    priority INTEGER DEFAULT 1,
    depth INTEGER DEFAULT 0,
    parent_page_id UUID REFERENCES pages(id),
    discovery_method VARCHAR DEFAULT 'crawl',
    last_seen_at TIMESTAMPTZ,
    metadata JSONB DEFAULT '{
        "inbound_links": 0,
        "outbound_links": 0,
        "word_count": 0,
        "has_canonical": false,
        "is_indexable": true
    }'::jsonb,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(website_id, url)
);

------ METRIC DEFINITIONS AND THRESHOLDS ------
CREATE TABLE metric_definitions (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    key VARCHAR NOT NULL UNIQUE,
    name VARCHAR NOT NULL,
    description TEXT NOT NULL,
    category metric_category NOT NULL,
    unit VARCHAR,
    is_core_metric BOOLEAN DEFAULT false,
    default_threshold NUMERIC,
    warning_threshold NUMERIC,
    critical_threshold NUMERIC,
    direction VARCHAR NOT NULL DEFAULT 'higher_is_better',
    weight NUMERIC DEFAULT 1.0,
    documentation_url TEXT,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Populate core metrics
INSERT INTO metric_definitions
(key, name, description, category, unit, is_core_metric, default_threshold, warning_threshold, critical_threshold, direction)
VALUES
-- Core Web Vitals
('performance', 'Performance Score', 'Overall performance score of the website', 'performance', '%', true, 90, 80, 70, 'higher_is_better'),
('accessibility', 'Accessibility Score', 'Overall accessibility score of the website', 'accessibility', '%', true, 90, 80, 70, 'higher_is_better'),
('seo', 'SEO Score', 'Overall SEO score of the website', 'seo', '%', true, 90, 80, 70, 'higher_is_better'),
('bestPractices', 'Best Practices Score', 'Overall best practices score', 'best_practices', '%', true, 90, 80, 70, 'higher_is_better'),

-- Performance Metrics
('firstContentfulPaint', 'First Contentful Paint', 'Time when the first text or image is painted', 'performance', 'ms', true, 1800, 2500, 4000, 'lower_is_better'),
('largestContentfulPaint', 'Largest Contentful Paint', 'Time when the largest text or image is painted', 'performance', 'ms', true, 2500, 4000, 6000, 'lower_is_better'),
('totalBlockingTime', 'Total Blocking Time', 'Sum of all time periods between FCP and Time to Interactive', 'performance', 'ms', true, 200, 400, 600, 'lower_is_better'),
('cumulativeLayoutShift', 'Cumulative Layout Shift', 'Measures visual stability', 'performance', 'score', true, 0.1, 0.25, 0.4, 'lower_is_better'),
('speedIndex', 'Speed Index', 'How quickly content is visually displayed', 'performance', 'ms', true, 3400, 5800, 8800, 'lower_is_better'),
('interactive', 'Time to Interactive', 'Time to fully interactive', 'performance', 'ms', true, 3800, 7300, 12700, 'lower_is_better'),

-- Resource Metrics
('totalByteWeight', 'Total Byte Weight', 'Total size of all resources', 'performance', 'bytes', false, 1600000, 2400000, 3200000, 'lower_is_better'),
('serverResponseTime', 'Server Response Time', 'Time for server to respond to main document request', 'performance', 'ms', false, 100, 200, 400, 'lower_is_better'),
('networkRtt', 'Network Round Trip Time', 'Network round trip time', 'performance', 'ms', false, 40, 100, 150, 'lower_is_better'),
('networkServerLatency', 'Network Server Latency', 'Server latency in network requests', 'performance', 'ms', false, 30, 100, 150, 'lower_is_better');

------ SCANS AND RESULTS ------
-- Scans table
CREATE TABLE scans (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    website_id UUID REFERENCES websites(id) NOT NULL,
    page_id UUID REFERENCES pages(id) NOT NULL,
    triggered_by UUID REFERENCES users(id),
    scan_type VARCHAR NOT NULL DEFAULT 'full',
    status scan_status DEFAULT 'pending',
    priority INTEGER DEFAULT 1,
    categories metric_category[] DEFAULT ARRAY['performance', 'seo', 'accessibility', 'best_practices'],
    device_type VARCHAR DEFAULT 'desktop',
    user_agent VARCHAR,
    lighthouse_version VARCHAR,
    chrome_version VARCHAR,
    environment JSONB DEFAULT '{}'::jsonb,
    started_at TIMESTAMPTZ DEFAULT NOW(),
    completed_at TIMESTAMPTZ,
    duration_ms INTEGER,
    error_message TEXT,
    retry_count INTEGER DEFAULT 0,
    metadata JSONB DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Scan results
CREATE TABLE scan_results (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    scan_id UUID REFERENCES scans(id) NOT NULL,
    category metric_category NOT NULL,
    score NUMERIC,
    raw_data JSONB,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Metric values
CREATE TABLE metric_values (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    scan_id UUID REFERENCES scans(id) NOT NULL,
    metric_id UUID REFERENCES metric_definitions(id) NOT NULL,
    value NUMERIC NOT NULL,
    raw_value VARCHAR,
    unit VARCHAR,
    is_passing BOOLEAN,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Resource analysis
CREATE TABLE resource_analysis (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    scan_id UUID REFERENCES scans(id) NOT NULL,
    resource_type resource_type NOT NULL,
    url VARCHAR NOT NULL,
    size_bytes INTEGER NOT NULL,
    transfer_size_bytes INTEGER,
    duration_ms INTEGER,
    is_third_party BOOLEAN DEFAULT false,
    is_cached BOOLEAN,
    compression_ratio NUMERIC,
    mime_type VARCHAR,
    protocol VARCHAR,
    priority VARCHAR,
    status_code INTEGER,
    metadata JSONB DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

------ MONITORING AND ALERTS ------
-- Alert configurations
CREATE TABLE alert_configurations (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    website_id UUID REFERENCES websites(id) NOT NULL,
    name VARCHAR NOT NULL,
    description TEXT,
    metric_id UUID REFERENCES metric_definitions(id) NOT NULL,
    threshold NUMERIC NOT NULL,
    comparison comparison_operator DEFAULT 'less_than',
    severity severity_level DEFAULT 'medium',
    consecutive_count INTEGER DEFAULT 1,
    cooldown_minutes INTEGER DEFAULT 60,
    notification_channels notification_channel[] DEFAULT ARRAY['email'],
    notification_template TEXT,
    is_active BOOLEAN DEFAULT true,
    last_triggered_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Alerts
CREATE TABLE alerts (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    website_id UUID REFERENCES websites(id) NOT NULL,
    page_id UUID REFERENCES pages(id),
    config_id UUID REFERENCES alert_configurations(id),
    metric_id UUID REFERENCES metric_definitions(id),
    severity severity_level DEFAULT 'medium',
    title VARCHAR NOT NULL,
    message TEXT NOT NULL,
    details JSONB DEFAULT '{}'::jsonb,
    status VARCHAR DEFAULT 'open',
    acknowledged_by UUID REFERENCES users(id),
    acknowledged_at TIMESTAMPTZ,
    resolved_at TIMESTAMPTZ,
    resolution_note TEXT,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Alert history
CREATE TABLE alert_history (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    alert_id UUID REFERENCES alerts(id) NOT NULL,
    event_type VARCHAR NOT NULL,
    event_data JSONB NOT NULL,
    created_by UUID REFERENCES users(id),
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Notification delivery
CREATE TABLE notification_deliveries (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    alert_id UUID REFERENCES alerts(id) NOT NULL,
    channel notification_channel NOT NULL,
    recipient VARCHAR NOT NULL,
    content TEXT NOT NULL,
    status VARCHAR DEFAULT 'pending',
    sent_at TIMESTAMPTZ,
    error_message TEXT,
    retry_count INTEGER DEFAULT 0,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

------ CRAWL MANAGEMENT ------
-- Crawl queue
CREATE TABLE crawl_queue (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    website_id UUID REFERENCES websites(id) NOT NULL,
    url VARCHAR NOT NULL,
    priority INTEGER DEFAULT 1,
    status VARCHAR DEFAULT 'pending',
    parent_url VARCHAR,
    discovery_depth INTEGER DEFAULT 0,
    attempts INTEGER DEFAULT 0,
    last_attempt_at TIMESTAMPTZ,
    next_attempt_at TIMESTAMPTZ,
    error_message TEXT,
    metadata JSONB DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Crawl sessions
CREATE TABLE crawl_sessions (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    website_id UUID REFERENCES websites(id) NOT NULL,
    status VARCHAR DEFAULT 'running',
    pages_discovered INTEGER DEFAULT 0,
    pages_processed INTEGER DEFAULT 0,
    start_url VARCHAR NOT NULL,
    max_depth INTEGER,
    started_at TIMESTAMPTZ DEFAULT NOW(),
    completed_at TIMESTAMPTZ,
    error_message TEXT,
    metadata JSONB DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- URL patterns
CREATE TABLE url_patterns (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    website_id UUID REFERENCES websites(id) NOT NULL,
    pattern VARCHAR NOT NULL,
    pattern_type VARCHAR NOT NULL, -- 'include' or 'exclude'
    description TEXT,
    is_regex BOOLEAN DEFAULT false,
    is_active BOOLEAN DEFAULT true,
    priority INTEGER DEFAULT 1,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

------ PERFORMANCE BUDGETS ------
CREATE TABLE performance_budgets (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    website_id UUID REFERENCES websites(id) NOT NULL,
    name VARCHAR NOT NULL,
    description TEXT,
    metric_id UUID REFERENCES metric_definitions(id),
    budget_type VARCHAR NOT NULL, -- 'size', 'timing', 'count'
    threshold NUMERIC NOT NULL,
    applies_to JSONB DEFAULT '{
        "resource_types": ["all"],
        "paths": ["/*"]
    }'::jsonb,
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Budget violations
CREATE TABLE budget_violations (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    budget_id UUID REFERENCES performance_budgets(id) NOT NULL,
    scan_id UUID REFERENCES scans(id) NOT NULL,
    actual_value NUMERIC NOT NULL,
    threshold_value NUMERIC NOT NULL,
    percentage_over NUMERIC,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

------ CUSTOM DASHBOARDS AND REPORTS ------
-- Dashboard definitions
CREATE TABLE dashboards (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    organization_id UUID REFERENCES organizations(id) NOT NULL,
    name VARCHAR NOT NULL,
    description TEXT,
    layout JSONB DEFAULT '[]'::jsonb,
    is_default BOOLEAN DEFAULT false,
    created_by UUID REFERENCES users(id),
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Dashboard widgets
CREATE TABLE dashboard_widgets (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    dashboard_id UUID REFERENCES dashboards(id) NOT NULL,
    widget_type VARCHAR NOT NULL,
    name VARCHAR NOT NULL,
    config JSONB NOT NULL,
    position JSONB NOT NULL,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Report templates
CREATE TABLE report_templates (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    organization_id UUID REFERENCES organizations(id) NOT NULL,
    name VARCHAR NOT NULL,
    description TEXT,
    template_type VARCHAR NOT NULL,
    content JSONB NOT NULL,
    schedule JSONB DEFAULT NULL,
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Generated reports
CREATE TABLE generated_reports (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    template_id UUID REFERENCES report_templates(id) NOT NULL,
    website_id UUID REFERENCES websites(id),
    generated_by UUID REFERENCES users(id),
    report_data JSONB NOT NULL,
    format VARCHAR DEFAULT 'pdf',
    file_url VARCHAR,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

------ API ACCESS AND RATE LIMITING ------
-- API keys
CREATE TABLE api_keys (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    organization_id UUID REFERENCES organizations(id) NOT NULL,
    name VARCHAR NOT NULL,
    key_hash VARCHAR NOT NULL,
    scopes VARCHAR[] DEFAULT ARRAY['read'],
    rate_limit_per_minute INTEGER DEFAULT 60,
    is_active BOOLEAN DEFAULT true,
    last_used_at TIMESTAMPTZ,
    expires_at TIMESTAMPTZ,
    created_by UUID REFERENCES users(id),
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Rate limiting
CREATE TABLE rate_limits (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    key_type VARCHAR NOT NULL, -- 'api_key', 'ip_address'
    key_value VARCHAR NOT NULL,
    window_start TIMESTAMPTZ NOT NULL,
    request_count INTEGER DEFAULT 1,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

------ AUDIT LOGGING ------
CREATE TABLE audit_logs (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    organization_id UUID REFERENCES organizations(id),
    user_id UUID REFERENCES users(id),
    action VARCHAR NOT NULL,
    entity_type VARCHAR NOT NULL,
    entity_id UUID,
    changes JSONB,
    ip_address VARCHAR,
    user_agent VARCHAR,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

------ VIEWS ------
-- Performance trend view
CREATE VIEW performance_trends AS
SELECT
    w.id AS website_id,
    w.name AS website_name,
    p.url AS page_url,
    m.key AS metric_key,
    mv.value AS metric_value,
    s.created_at AS scan_date,
    AVG(mv.value) OVER (
        PARTITION BY w.id, p.id, m.id
        ORDER BY s.created_at
        ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) AS rolling_average
FROM websites w
JOIN pages p ON p.website_id = w.id
JOIN scans s ON s.page_id = p.id
JOIN metric_values mv ON mv.scan_id = s.id
JOIN metric_definitions m ON m.id = mv.metric_id
WHERE s.created_at >= NOW() - INTERVAL '30 days';

-- Resource usage summary view
CREATE VIEW resource_usage_summary AS
SELECT
    w.id AS website_id,
    w.name AS website_name,
    ra.resource_type,
    COUNT(*) AS resource_count,
    AVG(ra.size_bytes) AS avg_size,
    SUM(ra.size_bytes) AS total_size,
    AVG(ra.duration_ms) AS avg_duration
FROM websites w
JOIN scans s ON s.website_id = w.id
JOIN resource_analysis ra ON ra.scan_id = s.id
WHERE s.created_at >= NOW() - INTERVAL '24 hours'
GROUP BY w.id, w.name, ra.resource_type;

------ FUNCTIONS ------
-- Calculate health score
CREATE OR REPLACE FUNCTION calculate_health_score(website_id UUID)
RETURNS NUMERIC AS $$
DECLARE
    score NUMERIC;
BEGIN
    SELECT
        AVG(
            CASE
                WHEN m.direction = 'higher_is_better' THEN
                    LEAST(mv.value / NULLIF(m.default_threshold, 0) * 100, 100)
                ELSE
                    LEAST(m.default_threshold / NULLIF(mv.value, 0) * 100, 100)
            END
        )
    INTO score
    FROM scans s
    JOIN metric_values mv ON mv.scan_id = s.id
    JOIN metric_definitions m ON m.id = mv.metric_id
    WHERE s.website_id = calculate_health_score.website_id
    AND s.created_at >= NOW() - INTERVAL '24 hours'
    AND m.is_core_metric = true;

    RETURN COALESCE(score, 0);
END;
$$ LANGUAGE plpgsql;

-- Update scan status
CREATE OR REPLACE FUNCTION update_scan_status()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.status = 'completed' THEN
        -- Update website's last scan timestamp
        UPDATE websites
        SET last_scan_at = NOW()
        WHERE id = NEW.website_id;

        -- Check for alerts
        INSERT INTO alerts (website_id, page_id, severity, title, message)
        SELECT
            NEW.website_id,
            NEW.page_id,
            ac.severity,
            'Metric threshold exceeded',
            format('%s is %s threshold of %s', m.name, ac.comparison, ac.threshold)
        FROM metric_values mv
        JOIN metric_definitions m ON m.id = mv.metric_id
        JOIN alert_configurations ac ON ac.metric_id = m.id
        WHERE mv.scan_id = NEW.id
        AND ac.website_id = NEW.website_id
        AND (
            CASE ac.comparison
                WHEN 'less_than' THEN mv.value < ac.threshold
                WHEN 'greater_than' THEN mv.value > ac.threshold
                WHEN 'equal_to' THEN mv.value = ac.threshold
            END
        );
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Create trigger for scan status updates
CREATE TRIGGER scan_status_update
    AFTER UPDATE OF status ON scans
    FOR EACH ROW
    WHEN (OLD.status IS DISTINCT FROM NEW.status)
    EXECUTE FUNCTION update_scan_status();

    ------ INDEXES ------
-- Performance indexes
CREATE INDEX idx_scans_website_status ON scans(website_id, status);
CREATE INDEX idx_scans_created_at ON scans(created_at);
CREATE INDEX idx_metric_values_scan_metric ON metric_values(scan_id, metric_id);
CREATE INDEX idx_pages_website_active ON pages(website_id, is_active);
CREATE INDEX idx_crawl_queue_status_priority ON crawl_queue(status, priority);
CREATE INDEX idx_alerts_website_status ON alerts(website_id, status);
CREATE INDEX idx_resource_analysis_scan ON resource_analysis(scan_id);
CREATE INDEX idx_audit_logs_organization ON audit_logs(organization_id, created_at);
CREATE INDEX idx_metric_values_created_at ON metric_values(created_at);
CREATE INDEX idx_pages_url_trgm ON pages USING gin (url gin_trgm_ops);

------ SECURITY POLICIES ------
-- RLS Policies
ALTER TABLE organizations ENABLE ROW LEVEL SECURITY;
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE websites ENABLE ROW LEVEL SECURITY;
ALTER TABLE pages ENABLE ROW LEVEL SECURITY;
ALTER TABLE scans ENABLE ROW LEVEL SECURITY;
ALTER TABLE metric_values ENABLE ROW LEVEL SECURITY;
ALTER TABLE alerts ENABLE ROW LEVEL SECURITY;
ALTER TABLE dashboards ENABLE ROW LEVEL SECURITY;
ALTER TABLE api_keys ENABLE ROW LEVEL SECURITY;

-- Organization access
CREATE POLICY "Users can view their organization"
    ON organizations
    FOR SELECT
    USING (id IN (
        SELECT organization_id FROM users WHERE id = auth.uid()
    ));

-- Website access
CREATE POLICY "Users can view their organization's websites"
    ON websites
    FOR SELECT
    USING (organization_id IN (
        SELECT organization_id FROM users WHERE id = auth.uid()
    ));

CREATE POLICY "Admins can manage their organization's websites"
    ON websites
    FOR ALL
    USING (
        organization_id IN (
            SELECT organization_id
            FROM users
            WHERE id = auth.uid() AND role IN ('admin', 'owner')
        )
    );

------ DATA RETENTION ------
-- Create retention policy function
CREATE OR REPLACE FUNCTION apply_data_retention()
RETURNS void AS $$
DECLARE
    org RECORD;
BEGIN
    -- Loop through organizations
    FOR org IN SELECT id, (settings->>'retention_days')::integer AS retention_days
               FROM organizations
               WHERE settings->>'retention_days' IS NOT NULL
    LOOP
        -- Delete old scan data
        DELETE FROM metric_values
        WHERE scan_id IN (
            SELECT id FROM scans
            WHERE website_id IN (
                SELECT id FROM websites WHERE organization_id = org.id
            )
            AND created_at < NOW() - (org.retention_days || ' days')::interval
        );

        -- Delete old resource analysis
        DELETE FROM resource_analysis
        WHERE scan_id IN (
            SELECT id FROM scans
            WHERE website_id IN (
                SELECT id FROM websites WHERE organization_id = org.id
            )
            AND created_at < NOW() - (org.retention_days || ' days')::interval
        );

        -- Delete old scans
        DELETE FROM scans
        WHERE website_id IN (
            SELECT id FROM websites WHERE organization_id = org.id
        )
        AND created_at < NOW() - (org.retention_days || ' days')::interval;

        -- Archive resolved alerts
        UPDATE alerts
        SET status = 'archived'
        WHERE website_id IN (
            SELECT id FROM websites WHERE organization_id = org.id
        )
        AND status = 'resolved'
        AND resolved_at < NOW() - (org.retention_days || ' days')::interval;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

------ ADDITIONAL FUNCTIONS ------
-- Calculate competitor comparison
CREATE OR REPLACE FUNCTION calculate_competitor_comparison(website_id UUID)
RETURNS TABLE (
    metric_key VARCHAR,
    your_score NUMERIC,
    competitor_avg NUMERIC,
    competitor_best NUMERIC,
    percentile NUMERIC
) AS $$
BEGIN
    RETURN QUERY
    WITH your_metrics AS (
        SELECT
            m.key,
            mv.value
        FROM scans s
        JOIN metric_values mv ON mv.scan_id = s.id
        JOIN metric_definitions m ON m.id = mv.metric_id
        WHERE s.website_id = calculate_competitor_comparison.website_id
        AND s.created_at = (
            SELECT MAX(created_at)
            FROM scans
            WHERE website_id = calculate_competitor_comparison.website_id
        )
    ),
    competitor_metrics AS (
        SELECT
            m.key,
            mv.value,
            PERCENT_RANK() OVER (PARTITION BY m.key ORDER BY mv.value) as percentile
        FROM scans s
        JOIN metric_values mv ON mv.scan_id = s.id
        JOIN metric_definitions m ON m.id = mv.metric_id
        WHERE s.website_id IN (
            SELECT competitor_url_id
            FROM competitor_websites
            WHERE website_id = calculate_competitor_comparison.website_id
        )
        AND s.created_at >= NOW() - INTERVAL '30 days'
    )
    SELECT
        ym.key,
        ym.value as your_score,
        AVG(cm.value) as competitor_avg,
        MAX(cm.value) as competitor_best,
        MAX(cm.percentile) * 100 as percentile
    FROM your_metrics ym
    LEFT JOIN competitor_metrics cm ON cm.key = ym.key
    GROUP BY ym.key, ym.value;
END;
$$ LANGUAGE plpgsql;

-- Generate performance report
CREATE OR REPLACE FUNCTION generate_performance_report(website_id UUID, days INTEGER)
RETURNS JSONB AS $$
DECLARE
    report JSONB;
BEGIN
    SELECT jsonb_build_object(
        'website_info', (
            SELECT jsonb_build_object(
                'name', name,
                'url', base_url,
                'report_period', jsonb_build_object(
                    'start', NOW() - (days || ' days')::interval,
                    'end', NOW()
                )
            )
            FROM websites
            WHERE id = website_id
        ),
        'performance_summary', (
            SELECT jsonb_build_object(
                'average_performance_score', AVG(mv.value),
                'best_performance_score', MAX(mv.value),
                'worst_performance_score', MIN(mv.value),
                'trend', jsonb_agg(
                    jsonb_build_object(
                        'date', DATE(s.created_at),
                        'score', mv.value
                    ) ORDER BY s.created_at
                )
            )
            FROM scans s
            JOIN metric_values mv ON mv.scan_id = s.id
            JOIN metric_definitions md ON md.id = mv.metric_id
            WHERE s.website_id = generate_performance_report.website_id
            AND md.key = 'performance'
            AND s.created_at >= NOW() - (days || ' days')::interval
        ),
        'core_metrics', (
            SELECT jsonb_object_agg(
                md.key,
                jsonb_build_object(
                    'average', AVG(mv.value),
                    'best', MAX(mv.value),
                    'worst', MIN(mv.value)
                )
            )
            FROM scans s
            JOIN metric_values mv ON mv.scan_id = s.id
            JOIN metric_definitions md ON md.id = mv.metric_id
            WHERE s.website_id = generate_performance_report.website_id
            AND md.is_core_metric = true
            AND s.created_at >= NOW() - (days || ' days')::interval
        ),
        'resource_summary', (
            SELECT jsonb_object_agg(
                resource_type,
                jsonb_build_object(
                    'count', COUNT(*),
                    'total_size', SUM(size_bytes),
                    'average_duration', AVG(duration_ms)
                )
            )
            FROM scans s
            JOIN resource_analysis ra ON ra.scan_id = s.id
            WHERE s.website_id = generate_performance_report.website_id
            AND s.created_at >= NOW() - (days || ' days')::interval
        ),
        'alerts', (
            SELECT jsonb_agg(
                jsonb_build_object(
                    'severity', severity,
                    'message', message,
                    'created_at', created_at
                )
            )
            FROM alerts
            WHERE website_id = generate_performance_report.website_id
            AND created_at >= NOW() - (days || ' days')::interval
        )
    ) INTO report;

    RETURN report;
END;
$$ LANGUAGE plpgsql;

------ NOTIFICATIONS ------
-- Create notification function
CREATE OR REPLACE FUNCTION process_alert_notifications()
RETURNS trigger AS $$
DECLARE
    website_record RECORD;
    user_record RECORD;
BEGIN
    -- Get website details
    SELECT * INTO website_record
    FROM websites
    WHERE id = NEW.website_id;

    -- Insert notification for each user in the organization
    FOR user_record IN
        SELECT u.*
        FROM users u
        WHERE u.organization_id = website_record.organization_id
        AND (u.settings->>'email_notifications')::boolean = true
    LOOP
        INSERT INTO notification_deliveries (
            alert_id,
            channel,
            recipient,
            content
        ) VALUES (
            NEW.id,
            'email',
            user_record.email,
            format(
                'Alert for %s: %s. Severity: %s',
                website_record.name,
                NEW.message,
                NEW.severity
            )
        );
    END LOOP;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Create trigger for alert notifications
CREATE TRIGGER alert_notification_trigger
    AFTER INSERT ON alerts
    FOR EACH ROW
    EXECUTE FUNCTION process_alert_notifications();

------ MAINTENANCE PROCEDURES ------
-- Create maintenance function
CREATE OR REPLACE FUNCTION perform_maintenance()
RETURNS void AS $$
BEGIN
    -- Clean up old rate limit records
    DELETE FROM rate_limits
    WHERE window_start < NOW() - INTERVAL '1 day';

    -- Archive old notifications
    UPDATE notification_deliveries
    SET status = 'archived'
    WHERE created_at < NOW() - INTERVAL '30 days';

    -- Clean up expired API keys
    UPDATE api_keys
    SET is_active = false
    WHERE expires_at < NOW();

    -- Update statistics
    ANALYZE websites;
    ANALYZE scans;
    ANALYZE metric_values;
    ANALYZE resource_analysis;

    -- Vacuum analyze for better query planning
    VACUUM ANALYZE websites;
    VACUUM ANALYZE scans;
    VACUUM ANALYZE metric_values;
    VACUUM ANALYZE resource_analysis;
END;
$$ LANGUAGE plpgsql;
