-- DBIS Core Lite Database Schema
-- PostgreSQL 14+

-- Operators (Terminal Users)
CREATE TABLE IF NOT EXISTS operators (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    operator_id VARCHAR(50) UNIQUE NOT NULL,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    role VARCHAR(50) NOT NULL CHECK (role IN ('MAKER', 'CHECKER', 'ADMIN')),
    active BOOLEAN DEFAULT TRUE,
    last_login_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_operators_operator_id ON operators(operator_id);
CREATE INDEX idx_operators_role ON operators(role);

-- Payments (Payment Transactions)
CREATE TABLE IF NOT EXISTS payments (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    payment_id VARCHAR(100) UNIQUE NOT NULL,
    type VARCHAR(50) NOT NULL CHECK (type IN ('CUSTOMER_CREDIT_TRANSFER', 'FI_TO_FI')),
    amount DECIMAL(18, 2) NOT NULL,
    currency VARCHAR(3) NOT NULL,
    sender_account VARCHAR(100) NOT NULL,
    sender_bic VARCHAR(11) NOT NULL,
    receiver_account VARCHAR(100) NOT NULL,
    receiver_bic VARCHAR(11) NOT NULL,
    beneficiary_name VARCHAR(255) NOT NULL,
    purpose TEXT,
    remittance_info TEXT,
    maker_operator_id UUID NOT NULL REFERENCES operators(id),
    checker_operator_id UUID REFERENCES operators(id),
    status VARCHAR(50) NOT NULL,
    internal_transaction_id VARCHAR(100),
    compliance_screening_id VARCHAR(100),
    compliance_status VARCHAR(20) CHECK (compliance_status IN ('PASS', 'FAIL', 'PENDING')),
    uetr UUID,
    iso_message_id VARCHAR(100),
    iso_message_hash VARCHAR(64),
    transport_session_id VARCHAR(100),
    ack_received BOOLEAN DEFAULT FALSE,
    nack_reason TEXT,
    settlement_confirmed BOOLEAN DEFAULT FALSE,
    settlement_date TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_payments_payment_id ON payments(payment_id);
CREATE INDEX idx_payments_status ON payments(status);
CREATE INDEX idx_payments_uetr ON payments(uetr);
CREATE INDEX idx_payments_maker ON payments(maker_operator_id);
CREATE INDEX idx_payments_created_at ON payments(created_at);

-- Ledger Postings (Core Banking Transactions)
CREATE TABLE IF NOT EXISTS ledger_postings (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    internal_transaction_id VARCHAR(100) UNIQUE NOT NULL,
    payment_id UUID NOT NULL REFERENCES payments(id),
    account_number VARCHAR(100) NOT NULL,
    transaction_type VARCHAR(20) NOT NULL CHECK (transaction_type IN ('DEBIT', 'CREDIT', 'RESERVE', 'RELEASE')),
    amount DECIMAL(18, 2) NOT NULL,
    currency VARCHAR(3) NOT NULL,
    status VARCHAR(20) NOT NULL CHECK (status IN ('PENDING', 'POSTED', 'FAILED', 'REVERSED')),
    posting_timestamp TIMESTAMP WITH TIME ZONE,
    reference TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_ledger_postings_transaction_id ON ledger_postings(internal_transaction_id);
CREATE INDEX idx_ledger_postings_payment_id ON ledger_postings(payment_id);
CREATE INDEX idx_ledger_postings_account ON ledger_postings(account_number);
CREATE INDEX idx_ledger_postings_status ON ledger_postings(status);

-- ISO Messages
CREATE TABLE IF NOT EXISTS iso_messages (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    message_id VARCHAR(100) UNIQUE NOT NULL,
    payment_id UUID NOT NULL REFERENCES payments(id),
    message_type VARCHAR(20) NOT NULL CHECK (message_type IN ('pacs.008', 'pacs.009')),
    uetr UUID NOT NULL,
    msg_id VARCHAR(100) NOT NULL,
    xml_content TEXT NOT NULL,
    xml_hash VARCHAR(64) NOT NULL,
    status VARCHAR(20) NOT NULL CHECK (status IN ('GENERATED', 'VALIDATED', 'TRANSMITTED', 'ACK_RECEIVED', 'NACK_RECEIVED')),
    transmitted_at TIMESTAMP WITH TIME ZONE,
    ack_received_at TIMESTAMP WITH TIME ZONE,
    nack_reason TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_iso_messages_message_id ON iso_messages(message_id);
CREATE INDEX idx_iso_messages_payment_id ON iso_messages(payment_id);
CREATE INDEX idx_iso_messages_uetr ON iso_messages(uetr);
CREATE INDEX idx_iso_messages_status ON iso_messages(status);

-- Transport Sessions (TLS Connections)
CREATE TABLE IF NOT EXISTS transport_sessions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id VARCHAR(100) UNIQUE NOT NULL,
    receiver_ip VARCHAR(45) NOT NULL,
    receiver_port INTEGER NOT NULL,
    tls_version VARCHAR(10),
    session_fingerprint VARCHAR(64),
    connected_at TIMESTAMP WITH TIME ZONE,
    disconnected_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_transport_sessions_session_id ON transport_sessions(session_id);
CREATE INDEX idx_transport_sessions_connected_at ON transport_sessions(connected_at);

-- ACK/NACK Logs
CREATE TABLE IF NOT EXISTS ack_nack_logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    message_id UUID NOT NULL REFERENCES iso_messages(id),
    payment_id UUID NOT NULL REFERENCES payments(id),
    uetr UUID NOT NULL,
    msg_id VARCHAR(100) NOT NULL,
    type VARCHAR(4) NOT NULL CHECK (type IN ('ACK', 'NACK')),
    payload TEXT NOT NULL,
    reason TEXT,
    received_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_ack_nack_logs_message_id ON ack_nack_logs(message_id);
CREATE INDEX idx_ack_nack_logs_payment_id ON ack_nack_logs(payment_id);
CREATE INDEX idx_ack_nack_logs_uetr ON ack_nack_logs(uetr);
CREATE INDEX idx_ack_nack_logs_received_at ON ack_nack_logs(received_at);

-- Settlement Records
CREATE TABLE IF NOT EXISTS settlement_records (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    payment_id UUID NOT NULL REFERENCES payments(id),
    uetr UUID NOT NULL,
    status VARCHAR(30) NOT NULL CHECK (status IN ('PENDING', 'ACK_RECEIVED', 'CREDIT_CONFIRMED', 'SETTLED', 'FAILED')),
    ack_received BOOLEAN DEFAULT FALSE,
    ack_received_at TIMESTAMP WITH TIME ZONE,
    credit_confirmed BOOLEAN DEFAULT FALSE,
    credit_confirmed_at TIMESTAMP WITH TIME ZONE,
    credit_confirmation_reference VARCHAR(100),
    settled_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_settlement_records_payment_id ON settlement_records(payment_id);
CREATE INDEX idx_settlement_records_uetr ON settlement_records(uetr);
CREATE INDEX idx_settlement_records_status ON settlement_records(status);

-- Reconciliation Runs
CREATE TABLE IF NOT EXISTS reconciliation_runs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    run_date DATE NOT NULL,
    run_timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    total_payments INTEGER DEFAULT 0,
    matched_payments INTEGER DEFAULT 0,
    unmatched_payments INTEGER DEFAULT 0,
    exceptions INTEGER DEFAULT 0,
    status VARCHAR(20) NOT NULL CHECK (status IN ('RUNNING', 'COMPLETED', 'FAILED')),
    completed_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_reconciliation_runs_run_date ON reconciliation_runs(run_date);
CREATE INDEX idx_reconciliation_runs_status ON reconciliation_runs(status);

-- Audit Logs (Tamper-evident)
CREATE TABLE IF NOT EXISTS audit_logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    event_type VARCHAR(100) NOT NULL,
    entity_type VARCHAR(50),
    entity_id VARCHAR(100),
    operator_id VARCHAR(50),
    terminal_id VARCHAR(100),
    action VARCHAR(100) NOT NULL,
    details JSONB,
    checksum VARCHAR(64) NOT NULL, -- SHA-256 of previous row + current row
    timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_audit_logs_event_type ON audit_logs(event_type);
CREATE INDEX idx_audit_logs_entity_type ON audit_logs(entity_type, entity_id);
CREATE INDEX idx_audit_logs_operator_id ON audit_logs(operator_id);
CREATE INDEX idx_audit_logs_timestamp ON audit_logs(timestamp);

-- Export History
CREATE TABLE IF NOT EXISTS export_history (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    format VARCHAR(20) NOT NULL CHECK (format IN ('rje', 'xmlv2', 'raw-iso', 'json')),
    scope VARCHAR(20) NOT NULL CHECK (scope IN ('messages', 'ledger', 'full')),
    record_count INTEGER NOT NULL DEFAULT 0,
    file_size BIGINT NOT NULL DEFAULT 0,
    filename VARCHAR(255) NOT NULL,
    start_date TIMESTAMP WITH TIME ZONE,
    end_date TIMESTAMP WITH TIME ZONE,
    account_number VARCHAR(100),
    uetr UUID,
    payment_id UUID,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_export_history_format ON export_history(format);
CREATE INDEX idx_export_history_scope ON export_history(scope);
CREATE INDEX idx_export_history_created_at ON export_history(created_at);
CREATE INDEX idx_export_history_uetr ON export_history(uetr);
CREATE INDEX idx_export_history_payment_id ON export_history(payment_id);

-- Function to update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = CURRENT_TIMESTAMP;
    RETURN NEW;
END;
$$ language 'plpgsql';

-- Triggers for updated_at
CREATE TRIGGER update_payments_updated_at BEFORE UPDATE ON payments
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_operators_updated_at BEFORE UPDATE ON operators
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_ledger_postings_updated_at BEFORE UPDATE ON ledger_postings
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_iso_messages_updated_at BEFORE UPDATE ON iso_messages
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_settlement_records_updated_at BEFORE UPDATE ON settlement_records
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
