-- ============================================================================
-- Migration 025: Ticket System & Klippekort Modul (Isoleret)
-- ============================================================================
-- Dette modul er 100% isoleret og kan slettes uden at påvirke eksisterende data.
-- Alle tabeller har prefix 'tticket_' for at markere tilhørsforhold til modulet.
-- Ved uninstall køres DROP-scriptet i bunden af denne fil.
-- ============================================================================

-- Metadata tabel til at tracke modulets tilstand
CREATE TABLE IF NOT EXISTS tticket_metadata (
    id SERIAL PRIMARY KEY,
    module_version VARCHAR(20) NOT NULL DEFAULT '1.0.0',
    installed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    installed_by INTEGER,  -- Reference til users.user_id (read-only, ingen FK)
    last_sync_at TIMESTAMP,
    is_active BOOLEAN DEFAULT true,
    settings JSONB DEFAULT '{}'::jsonb
);

-- Indsæt initial metadata
INSERT INTO tticket_metadata (module_version, is_active) 
VALUES ('1.0.0', true)
ON CONFLICT DO NOTHING;

-- ============================================================================
-- TICKETS (hovedtabel)
-- ============================================================================
CREATE TABLE IF NOT EXISTS tticket_tickets (
    id SERIAL PRIMARY KEY,
    ticket_number VARCHAR(50) UNIQUE NOT NULL,  -- Format: TKT-YYYYMMDD-XXX
    
    -- Core felter
    subject VARCHAR(500) NOT NULL,
    description TEXT,
    status VARCHAR(20) DEFAULT 'open' CHECK (status IN ('open', 'in_progress', 'waiting_customer', 'waiting_internal', 'resolved', 'closed')),
    priority VARCHAR(20) DEFAULT 'normal' CHECK (priority IN ('low', 'normal', 'high', 'urgent')),
    category VARCHAR(100),  -- support, bug, feature_request, question, etc.
    
    -- Relationer (read-only references - INGEN FK til core tables)
    customer_id INTEGER,  -- Reference til customers.id
    contact_id INTEGER,   -- Reference til contacts.id
    assigned_to_user_id INTEGER,  -- Reference til users.user_id
    created_by_user_id INTEGER,   -- Reference til users.user_id
    
    -- Kilde
    source VARCHAR(50) DEFAULT 'manual' CHECK (source IN ('email', 'portal', 'phone', 'manual', 'api')),
    
    -- Metadata
    tags TEXT[],
    custom_fields JSONB,
    
    -- Timestamps
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP,
    first_response_at TIMESTAMP,  -- For SLA tracking (future)
    resolved_at TIMESTAMP,
    closed_at TIMESTAMP
);

CREATE INDEX idx_tticket_tickets_number ON tticket_tickets(ticket_number);
CREATE INDEX idx_tticket_tickets_customer ON tticket_tickets(customer_id);
CREATE INDEX idx_tticket_tickets_assigned ON tticket_tickets(assigned_to_user_id);
CREATE INDEX idx_tticket_tickets_status ON tticket_tickets(status);
CREATE INDEX idx_tticket_tickets_priority ON tticket_tickets(priority);
CREATE INDEX idx_tticket_tickets_created ON tticket_tickets(created_at DESC);

-- ============================================================================
-- COMMENTS (beskeder på tickets)
-- ============================================================================
CREATE TABLE IF NOT EXISTS tticket_comments (
    id SERIAL PRIMARY KEY,
    ticket_id INTEGER NOT NULL REFERENCES tticket_tickets(id) ON DELETE CASCADE,
    user_id INTEGER,  -- Reference til users.user_id (read-only, ingen FK)
    comment_text TEXT NOT NULL,
    is_internal BOOLEAN DEFAULT false,  -- Intern note vs. kunde-synlig
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP
);

CREATE INDEX idx_tticket_comments_ticket ON tticket_comments(ticket_id);
CREATE INDEX idx_tticket_comments_created ON tticket_comments(created_at);

-- ============================================================================
-- ATTACHMENTS (filer på tickets)
-- ============================================================================
CREATE TABLE IF NOT EXISTS tticket_attachments (
    id SERIAL PRIMARY KEY,
    ticket_id INTEGER NOT NULL REFERENCES tticket_tickets(id) ON DELETE CASCADE,
    file_name VARCHAR(255) NOT NULL,
    file_path VARCHAR(500) NOT NULL,
    file_size INTEGER,
    mime_type VARCHAR(100),
    uploaded_by_user_id INTEGER,  -- Reference til users.user_id (read-only)
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_tticket_attachments_ticket ON tticket_attachments(ticket_id);

-- ============================================================================
-- WORKLOG (tidsregistrering på tickets)
-- ============================================================================
CREATE TABLE IF NOT EXISTS tticket_worklog (
    id SERIAL PRIMARY KEY,
    ticket_id INTEGER NOT NULL REFERENCES tticket_tickets(id) ON DELETE CASCADE,
    
    -- Arbejdsdata
    work_date DATE NOT NULL,
    hours DECIMAL(5,2) NOT NULL CHECK (hours > 0),
    work_type VARCHAR(50) DEFAULT 'support' CHECK (work_type IN ('support', 'development', 'troubleshooting', 'on_site', 'meeting', 'other')),
    description TEXT,
    
    -- Fakturering
    billing_method VARCHAR(20) DEFAULT 'invoice' CHECK (billing_method IN ('prepaid_card', 'invoice', 'internal', 'warranty')),
    status VARCHAR(20) DEFAULT 'draft' CHECK (status IN ('draft', 'billable', 'billed', 'non_billable')),
    prepaid_card_id INTEGER,  -- Reference til tticket_prepaid_cards (read-only)
    
    -- Metadata
    user_id INTEGER,  -- Reference til users.user_id (read-only)
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP,
    billed_at TIMESTAMP
);

CREATE INDEX idx_tticket_worklog_ticket ON tticket_worklog(ticket_id);
CREATE INDEX idx_tticket_worklog_status ON tticket_worklog(status);
CREATE INDEX idx_tticket_worklog_date ON tticket_worklog(work_date);
CREATE INDEX idx_tticket_worklog_card ON tticket_worklog(prepaid_card_id);

-- ============================================================================
-- PREPAID CARDS (klippekort - kun 1 aktivt per virksomhed)
-- ============================================================================
CREATE TABLE IF NOT EXISTS tticket_prepaid_cards (
    id SERIAL PRIMARY KEY,
    card_number VARCHAR(50) UNIQUE NOT NULL,  -- Format: CARD-YYYYMMDD-XXX
    
    -- Tilknytning (kun 1 aktivt kort per customer)
    customer_id INTEGER NOT NULL,  -- Reference til customers.id (read-only, ingen FK)
    
    -- Saldo
    purchased_hours DECIMAL(8,2) NOT NULL CHECK (purchased_hours > 0),
    used_hours DECIMAL(8,2) DEFAULT 0 CHECK (used_hours >= 0),
    remaining_hours DECIMAL(8,2) GENERATED ALWAYS AS (purchased_hours - used_hours) STORED,
    
    -- Priser
    price_per_hour DECIMAL(10,2) NOT NULL,
    total_amount DECIMAL(12,2) NOT NULL,
    
    -- Lifecycle
    status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'depleted', 'expired', 'cancelled')),
    purchased_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    expires_at TIMESTAMP,  -- NULL = ingen udløb
    
    -- e-conomic integration
    economic_invoice_number VARCHAR(50),
    economic_product_number VARCHAR(50),
    
    -- Metadata
    notes TEXT,
    created_by_user_id INTEGER,  -- Reference til users.user_id (read-only)
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP
);

-- CRITICAL: Kun 1 aktivt kort per customer
CREATE UNIQUE INDEX idx_tticket_prepaid_unique_active ON tticket_prepaid_cards(customer_id) 
WHERE status = 'active';

CREATE INDEX idx_tticket_prepaid_customer ON tticket_prepaid_cards(customer_id);
CREATE INDEX idx_tticket_prepaid_status ON tticket_prepaid_cards(status);
CREATE INDEX idx_tticket_prepaid_expires ON tticket_prepaid_cards(expires_at);

-- ============================================================================
-- PREPAID TRANSACTIONS (immutable log af forbrug)
-- ============================================================================
CREATE TABLE IF NOT EXISTS tticket_prepaid_transactions (
    id SERIAL PRIMARY KEY,
    card_id INTEGER NOT NULL REFERENCES tticket_prepaid_cards(id) ON DELETE CASCADE,
    worklog_id INTEGER,  -- Reference til tticket_worklog (read-only, NULL for purchases/top-ups)
    
    -- Transaction type
    transaction_type VARCHAR(20) NOT NULL CHECK (transaction_type IN ('purchase', 'top_up', 'usage', 'refund', 'expiration', 'cancellation')),
    
    -- Beløb (positiv = tilføj, negativ = træk)
    hours DECIMAL(8,2) NOT NULL,
    balance_after DECIMAL(8,2) NOT NULL,
    
    -- Beskrivelse
    description TEXT,
    
    -- Metadata (immutable)
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    created_by_user_id INTEGER  -- Reference til users.user_id (read-only)
);

CREATE INDEX idx_tticket_transactions_card ON tticket_prepaid_transactions(card_id);
CREATE INDEX idx_tticket_transactions_worklog ON tticket_prepaid_transactions(worklog_id);
CREATE INDEX idx_tticket_transactions_created ON tticket_prepaid_transactions(created_at DESC);

-- ============================================================================
-- EMAIL INTEGRATION LOG (email → ticket mapping)
-- ============================================================================
CREATE TABLE IF NOT EXISTS tticket_email_log (
    id SERIAL PRIMARY KEY,
    ticket_id INTEGER REFERENCES tticket_tickets(id) ON DELETE CASCADE,
    email_id INTEGER,  -- Reference til email_messages.id (read-only, ingen FK)
    email_message_id VARCHAR(500),  -- Email Message-ID header for threading
    action VARCHAR(50) NOT NULL,  -- created|comment_added|updated|linked
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_tticket_email_log_ticket ON tticket_email_log(ticket_id);
CREATE INDEX idx_tticket_email_log_email ON tticket_email_log(email_id);
CREATE INDEX idx_tticket_email_log_message_id ON tticket_email_log(email_message_id);

-- ============================================================================
-- AUDIT LOG (alle ændringer)
-- ============================================================================
CREATE TABLE IF NOT EXISTS tticket_audit_log (
    id SERIAL PRIMARY KEY,
    ticket_id INTEGER,  -- NULL for system-level events
    entity_type VARCHAR(50) NOT NULL,  -- ticket, comment, worklog, prepaid_card, etc.
    entity_id INTEGER,
    user_id INTEGER,  -- Reference til users.user_id (read-only)
    action VARCHAR(50) NOT NULL,  -- created, updated, deleted, status_changed, assigned, etc.
    old_value TEXT,
    new_value TEXT,
    details JSONB,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_tticket_audit_ticket ON tticket_audit_log(ticket_id);
CREATE INDEX idx_tticket_audit_entity ON tticket_audit_log(entity_type, entity_id);
CREATE INDEX idx_tticket_audit_created ON tticket_audit_log(created_at DESC);

-- ============================================================================
-- FUNCTIONS
-- ============================================================================

-- Auto-update timestamp function
CREATE OR REPLACE FUNCTION tticket_update_timestamp()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = CURRENT_TIMESTAMP;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Generate ticket number function
CREATE OR REPLACE FUNCTION tticket_generate_ticket_number()
RETURNS VARCHAR AS $$
DECLARE
    today_str VARCHAR(8);
    last_number INTEGER;
    next_number INTEGER;
    new_ticket_number VARCHAR(50);
BEGIN
    -- Format: TKT-YYYYMMDD-XXX
    today_str := TO_CHAR(CURRENT_DATE, 'YYYYMMDD');
    
    -- Find last ticket number for today
    SELECT COALESCE(
        MAX(CAST(SPLIT_PART(ticket_number, '-', 3) AS INTEGER)), 
        0
    ) INTO last_number
    FROM tticket_tickets
    WHERE ticket_number LIKE 'TKT-' || today_str || '-%';
    
    next_number := last_number + 1;
    new_ticket_number := 'TKT-' || today_str || '-' || LPAD(next_number::TEXT, 3, '0');
    
    RETURN new_ticket_number;
END;
$$ LANGUAGE plpgsql;

-- Generate prepaid card number function
CREATE OR REPLACE FUNCTION tticket_generate_card_number()
RETURNS VARCHAR AS $$
DECLARE
    today_str VARCHAR(8);
    last_number INTEGER;
    next_number INTEGER;
    new_card_number VARCHAR(50);
BEGIN
    -- Format: CARD-YYYYMMDD-XXX
    today_str := TO_CHAR(CURRENT_DATE, 'YYYYMMDD');
    
    -- Find last card number for today
    SELECT COALESCE(
        MAX(CAST(SPLIT_PART(card_number, '-', 3) AS INTEGER)), 
        0
    ) INTO last_number
    FROM tticket_prepaid_cards
    WHERE card_number LIKE 'CARD-' || today_str || '-%';
    
    next_number := last_number + 1;
    new_card_number := 'CARD-' || today_str || '-' || LPAD(next_number::TEXT, 3, '0');
    
    RETURN new_card_number;
END;
$$ LANGUAGE plpgsql;

-- ============================================================================
-- TRIGGERS
-- ============================================================================

-- Auto-update timestamps
CREATE TRIGGER tticket_tickets_update
    BEFORE UPDATE ON tticket_tickets
    FOR EACH ROW EXECUTE FUNCTION tticket_update_timestamp();

CREATE TRIGGER tticket_comments_update
    BEFORE UPDATE ON tticket_comments
    FOR EACH ROW EXECUTE FUNCTION tticket_update_timestamp();

CREATE TRIGGER tticket_worklog_update
    BEFORE UPDATE ON tticket_worklog
    FOR EACH ROW EXECUTE FUNCTION tticket_update_timestamp();

CREATE TRIGGER tticket_prepaid_cards_update
    BEFORE UPDATE ON tticket_prepaid_cards
    FOR EACH ROW EXECUTE FUNCTION tticket_update_timestamp();

-- Auto-generate ticket number if not provided
CREATE OR REPLACE FUNCTION tticket_auto_generate_ticket_number()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.ticket_number IS NULL OR NEW.ticket_number = '' THEN
        NEW.ticket_number := tticket_generate_ticket_number();
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tticket_tickets_generate_number
    BEFORE INSERT ON tticket_tickets
    FOR EACH ROW EXECUTE FUNCTION tticket_auto_generate_ticket_number();

-- Auto-generate card number if not provided
CREATE OR REPLACE FUNCTION tticket_auto_generate_card_number()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.card_number IS NULL OR NEW.card_number = '' THEN
        NEW.card_number := tticket_generate_card_number();
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tticket_prepaid_cards_generate_number
    BEFORE INSERT ON tticket_prepaid_cards
    FOR EACH ROW EXECUTE FUNCTION tticket_auto_generate_card_number();

-- ============================================================================
-- VIEWS (common queries)
-- ============================================================================

-- View: Open tickets with statistics
CREATE OR REPLACE VIEW tticket_open_tickets AS
SELECT 
    t.*,
    COUNT(DISTINCT c.id) AS comment_count,
    COUNT(DISTINCT a.id) AS attachment_count,
    SUM(w.hours) FILTER (WHERE w.status IN ('draft', 'billable')) AS pending_hours,
    SUM(w.hours) FILTER (WHERE w.status = 'billed') AS billed_hours,
    MAX(c.created_at) AS last_comment_at,
    EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - t.created_at))/3600 AS age_hours
FROM tticket_tickets t
LEFT JOIN tticket_comments c ON t.id = c.ticket_id
LEFT JOIN tticket_attachments a ON t.id = a.ticket_id
LEFT JOIN tticket_worklog w ON t.id = w.ticket_id
WHERE t.status IN ('open', 'in_progress', 'waiting_customer', 'waiting_internal')
GROUP BY t.id;

-- View: Worklog entries ready for billing
CREATE OR REPLACE VIEW tticket_billable_worklog AS
SELECT 
    w.*,
    t.ticket_number,
    t.subject AS ticket_subject,
    t.customer_id,
    t.status AS ticket_status,
    CASE 
        WHEN w.billing_method = 'prepaid_card' THEN pc.remaining_hours >= w.hours
        ELSE true
    END AS has_sufficient_balance
FROM tticket_worklog w
JOIN tticket_tickets t ON w.ticket_id = t.id
LEFT JOIN tticket_prepaid_cards pc ON w.prepaid_card_id = pc.id
WHERE w.status = 'billable';

-- View: Prepaid card balances
CREATE OR REPLACE VIEW tticket_prepaid_balances AS
SELECT 
    pc.*,
    COUNT(w.id) AS usage_count,
    SUM(w.hours) AS total_hours_used,
    COUNT(w.id) FILTER (WHERE w.status = 'billed') AS billed_usage_count
FROM tticket_prepaid_cards pc
LEFT JOIN tticket_worklog w ON pc.id = w.prepaid_card_id
GROUP BY pc.id;

-- View: Ticket statistics by status
CREATE OR REPLACE VIEW tticket_stats_by_status AS
SELECT 
    status,
    priority,
    COUNT(*) AS ticket_count,
    AVG(EXTRACT(EPOCH FROM (COALESCE(resolved_at, CURRENT_TIMESTAMP) - created_at))/3600) AS avg_age_hours
FROM tticket_tickets
GROUP BY status, priority;

-- ============================================================================
-- INITIAL DATA
-- ============================================================================

-- Log installation in audit log
INSERT INTO tticket_audit_log (entity_type, action, details)
VALUES (
    'system', 
    'module_installed', 
    jsonb_build_object(
        'version', '1.0.0',
        'timestamp', CURRENT_TIMESTAMP,
        'tables_created', ARRAY[
            'tticket_metadata', 'tticket_tickets', 'tticket_comments', 
            'tticket_attachments', 'tticket_worklog', 'tticket_prepaid_cards',
            'tticket_prepaid_transactions', 'tticket_email_log', 'tticket_audit_log'
        ]
    )
);

-- ============================================================================
-- UNINSTALL SCRIPT (bruges ved modul-sletning)
-- ============================================================================
-- ADVARSEL: Dette script sletter ALLE data i modulet!
-- Kør kun hvis modulet skal fjernes fuldstændigt.
-- 
-- For at uninstalle, kør følgende kommandoer i rækkefølge:
--
-- -- Drop views
-- DROP VIEW IF EXISTS tticket_stats_by_status CASCADE;
-- DROP VIEW IF EXISTS tticket_prepaid_balances CASCADE;
-- DROP VIEW IF EXISTS tticket_billable_worklog CASCADE;
-- DROP VIEW IF EXISTS tticket_open_tickets CASCADE;
-- 
-- -- Drop triggers
-- DROP TRIGGER IF EXISTS tticket_prepaid_cards_generate_number ON tticket_prepaid_cards;
-- DROP TRIGGER IF EXISTS tticket_tickets_generate_number ON tticket_tickets;
-- DROP TRIGGER IF EXISTS tticket_prepaid_cards_update ON tticket_prepaid_cards;
-- DROP TRIGGER IF EXISTS tticket_worklog_update ON tticket_worklog;
-- DROP TRIGGER IF EXISTS tticket_comments_update ON tticket_comments;
-- DROP TRIGGER IF EXISTS tticket_tickets_update ON tticket_tickets;
-- 
-- -- Drop functions
-- DROP FUNCTION IF EXISTS tticket_generate_card_number() CASCADE;
-- DROP FUNCTION IF EXISTS tticket_generate_ticket_number() CASCADE;
-- DROP FUNCTION IF EXISTS tticket_auto_generate_card_number() CASCADE;
-- DROP FUNCTION IF EXISTS tticket_auto_generate_ticket_number() CASCADE;
-- DROP FUNCTION IF EXISTS tticket_update_timestamp() CASCADE;
-- 
-- -- Drop tables (reverse dependency order)
-- DROP TABLE IF EXISTS tticket_audit_log CASCADE;
-- DROP TABLE IF EXISTS tticket_email_log CASCADE;
-- DROP TABLE IF EXISTS tticket_prepaid_transactions CASCADE;
-- DROP TABLE IF EXISTS tticket_prepaid_cards CASCADE;
-- DROP TABLE IF EXISTS tticket_worklog CASCADE;
-- DROP TABLE IF EXISTS tticket_attachments CASCADE;
-- DROP TABLE IF EXISTS tticket_comments CASCADE;
-- DROP TABLE IF EXISTS tticket_tickets CASCADE;
-- DROP TABLE IF EXISTS tticket_metadata CASCADE;
-- 
-- -- Log uninstall
-- -- (Dette vil fejle hvis tticket_audit_log er droppet, men det er OK)
-- DO $$
-- BEGIN
--     IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'tticket_audit_log') THEN
--         INSERT INTO tticket_audit_log (entity_type, action, details)
--         VALUES ('system', 'module_uninstalled', jsonb_build_object('timestamp', CURRENT_TIMESTAMP));
--     END IF;
-- EXCEPTION WHEN OTHERS THEN
--     NULL;  -- Ignorer fejl
-- END $$;
-- 
-- ============================================================================
