-- ITSM-Plattform: PostgreSQL-Schema
-- Mandantenfaehig (mehrere Organisationen auf einer Instanz, Daten per tenant_id getrennt).
-- Enthaelt audit_log fuer Nachvollziehbarkeit gemaess ISO 27001 A.12.4 / DSGVO Art. 30.

CREATE TABLE IF NOT EXISTS tenants (
    id                      SERIAL PRIMARY KEY,
    name                    TEXT NOT NULL,
    created_at              TIMESTAMPTZ NOT NULL DEFAULT now(),
    dsb_name                TEXT,               -- Datenschutzbeauftragter/Kontakt
    dsb_email               TEXT,
    retention_tickets_days  INTEGER NOT NULL DEFAULT 1095,   -- DSGVO Speicherbegrenzung (Default ~3 Jahre)
    retention_audit_days    INTEGER NOT NULL DEFAULT 1825,   -- Audit-Log-Aufbewahrung (Default ~5 Jahre)
    sla_antwort_minuten     INTEGER NOT NULL DEFAULT 480,
    sla_loesung_minuten     INTEGER NOT NULL DEFAULT 2880
);

CREATE TABLE IF NOT EXISTS users (
    id             SERIAL PRIMARY KEY,
    tenant_id      INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    email          TEXT NOT NULL UNIQUE,
    password_hash  TEXT NOT NULL,
    role           TEXT NOT NULL DEFAULT 'agent',   -- 'admin' | 'agent'
    active         BOOLEAN NOT NULL DEFAULT TRUE,
    created_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
    last_login_at  TIMESTAMPTZ
);

CREATE TABLE IF NOT EXISTS services (
    id            SERIAL PRIMARY KEY,
    tenant_id     INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    name          TEXT NOT NULL,
    beschreibung  TEXT,
    kategorie     TEXT,
    gebucht       BOOLEAN NOT NULL DEFAULT TRUE,
    endpoint      TEXT,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS tickets (
    id                   SERIAL PRIMARY KEY,
    tenant_id            INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    ticket_nr            TEXT NOT NULL,
    titel                TEXT NOT NULL,
    beschreibung         TEXT,
    service_id           INTEGER REFERENCES services(id) ON DELETE SET NULL,
    status               TEXT NOT NULL DEFAULT 'Offen',
    prioritaet           TEXT NOT NULL DEFAULT 'Mittel',
    kategorie            TEXT NOT NULL DEFAULT 'Task',
    zugewiesen_an        TEXT,
    ersteller_id         INTEGER REFERENCES users(id) ON DELETE SET NULL,
    fortschritt          INTEGER NOT NULL DEFAULT 0,
    sla_antwort_minuten  INTEGER,
    sla_loesung_minuten  INTEGER,
    created_at           TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at           TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (tenant_id, ticket_nr)
);

CREATE TABLE IF NOT EXISTS ticket_timeline (
    id         SERIAL PRIMARY KEY,
    ticket_id  INTEGER NOT NULL REFERENCES tickets(id) ON DELETE CASCADE,
    tenant_id  INTEGER NOT NULL,
    zeit       TIMESTAMPTZ NOT NULL DEFAULT now(),
    akteur     TEXT,
    text       TEXT
);

CREATE TABLE IF NOT EXISTS audit_log (
    id          SERIAL PRIMARY KEY,
    tenant_id   INTEGER REFERENCES tenants(id) ON DELETE CASCADE,
    user_id     INTEGER REFERENCES users(id) ON DELETE SET NULL,
    zeit        TIMESTAMPTZ NOT NULL DEFAULT now(),
    aktion      TEXT NOT NULL,
    entity_typ  TEXT,
    entity_id   TEXT,
    details     JSONB,
    ip          TEXT
);

CREATE INDEX IF NOT EXISTS idx_tickets_tenant_status ON tickets (tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_tickets_tenant_kategorie ON tickets (tenant_id, kategorie);
CREATE INDEX IF NOT EXISTS idx_timeline_ticket ON ticket_timeline (ticket_id);
CREATE INDEX IF NOT EXISTS idx_audit_tenant_zeit ON audit_log (tenant_id, zeit DESC);
CREATE INDEX IF NOT EXISTS idx_services_tenant ON services (tenant_id);
CREATE INDEX IF NOT EXISTS idx_users_tenant ON users (tenant_id);

-- Vorbereitung fuer spaetere AD/LDAP-Anbindung (Nutzer-Vorgabe 2026-07-13):
-- 'local' = im ITSM selbst verwaltetes Konto (heutiger Stand), 'ldap' reserviert
-- fuer die kommende Ausbaustufe. Kein Verhaltensunterschied heute, nur Vorbereitung.
ALTER TABLE users ADD COLUMN IF NOT EXISTS auth_source TEXT NOT NULL DEFAULT 'local';

-- Zusaetzliche Nutzer-Details (Nutzer-Vorgabe 2026-07-13: "Mehr Details fuer
-- die User (Telefon, Adresse, etc.)"). Alle optional, da nicht jeder Mandant
-- diese Angaben pflegen moechte.
ALTER TABLE users ADD COLUMN IF NOT EXISTS vorname TEXT;
ALTER TABLE users ADD COLUMN IF NOT EXISTS nachname TEXT;
ALTER TABLE users ADD COLUMN IF NOT EXISTS telefon TEXT;
ALTER TABLE users ADD COLUMN IF NOT EXISTS abteilung TEXT;
ALTER TABLE users ADD COLUMN IF NOT EXISTS adresse TEXT;

-- ── Wissensdatenbank ──────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS kb_articles (
    id             SERIAL PRIMARY KEY,
    tenant_id      INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    titel          TEXT NOT NULL,
    kategorie      TEXT NOT NULL DEFAULT 'Allgemein',
    inhalt         TEXT NOT NULL,
    tags           TEXT,
    autor_user_id  INTEGER REFERENCES users(id) ON DELETE SET NULL,
    created_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at     TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_kb_tenant_kategorie ON kb_articles (tenant_id, kategorie);

-- ── CMDB (Configuration Management Database) ──────────────────────────────────
CREATE TABLE IF NOT EXISTS configuration_items (
    id            SERIAL PRIMARY KEY,
    tenant_id     INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    name          TEXT NOT NULL,
    ci_typ        TEXT NOT NULL DEFAULT 'Sonstiges',
    status        TEXT NOT NULL DEFAULT 'Aktiv',
    beschreibung  TEXT,
    attribute     JSONB NOT NULL DEFAULT '{}'::jsonb,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_ci_tenant_typ ON configuration_items (tenant_id, ci_typ);
CREATE INDEX IF NOT EXISTS idx_ci_tenant_status ON configuration_items (tenant_id, status);

CREATE TABLE IF NOT EXISTS ci_relationships (
    id              SERIAL PRIMARY KEY,
    tenant_id       INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    from_ci_id      INTEGER NOT NULL REFERENCES configuration_items(id) ON DELETE CASCADE,
    to_ci_id        INTEGER NOT NULL REFERENCES configuration_items(id) ON DELETE CASCADE,
    beziehungs_typ  TEXT NOT NULL DEFAULT 'haengt ab von',
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_cirel_tenant ON ci_relationships (tenant_id);
CREATE INDEX IF NOT EXISTS idx_cirel_from ON ci_relationships (from_ci_id);
CREATE INDEX IF NOT EXISTS idx_cirel_to ON ci_relationships (to_ci_id);


-- ═══════════════════════════════════════════════════════════════════════════════
-- Migration 2026-07-15: ITIL-v3/v4-Ausrichtung + Sicherheits-Haertung (Rust-Rewrite)
-- Alle Aenderungen idempotent, damit init beim App-Start gefahrlos wiederholt laeuft.
-- ═══════════════════════════════════════════════════════════════════════════════

-- ── Rollen (ITIL-orientiert) ──────────────────────────────────────────────────
-- 'admin' (Service Owner), 'change_manager' (Change Enablement/CAB),
-- 'agent' (Service Desk), 'user' (Requester/Self-Service).
-- users.role existiert bereits als TEXT; erlaubte Werte prueft die App.

-- ── Tickets: Impact/Urgency-Prioritaetsmatrix (ITIL v3, SO 4.2.5.4) ──────────
ALTER TABLE tickets ADD COLUMN IF NOT EXISTS impact  TEXT NOT NULL DEFAULT 'Mittel';
ALTER TABLE tickets ADD COLUMN IF NOT EXISTS urgency TEXT NOT NULL DEFAULT 'Mittel';

-- ── Tickets: Change Enablement (v4) / Change Management (v3) ──────────────────
ALTER TABLE tickets ADD COLUMN IF NOT EXISTS change_typ      TEXT;
ALTER TABLE tickets ADD COLUMN IF NOT EXISTS approval_status TEXT;
ALTER TABLE tickets ADD COLUMN IF NOT EXISTS approved_by     INTEGER REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE tickets ADD COLUMN IF NOT EXISTS approved_at     TIMESTAMPTZ;

-- ── Tickets: Problem Management (v3 SO 4.4 / v4 Practice) ─────────────────────
ALTER TABLE tickets ADD COLUMN IF NOT EXISTS problem_id  INTEGER REFERENCES tickets(id) ON DELETE SET NULL;
ALTER TABLE tickets ADD COLUMN IF NOT EXISTS known_error BOOLEAN NOT NULL DEFAULT FALSE;
CREATE INDEX IF NOT EXISTS idx_tickets_problem ON tickets (problem_id);

-- ── Tickets: SLA-Zeitstempel (Service Level Management) ───────────────────────
ALTER TABLE tickets ADD COLUMN IF NOT EXISTS first_response_at TIMESTAMPTZ;
ALTER TABLE tickets ADD COLUMN IF NOT EXISTS resolved_at       TIMESTAMPTZ;
ALTER TABLE tickets ADD COLUMN IF NOT EXISTS closed_at         TIMESTAMPTZ;

-- ── Ticket-CI-Verknuepfung (SACM v3 / Service Configuration Management v4) ────
CREATE TABLE IF NOT EXISTS ticket_ci_links (
    id         SERIAL PRIMARY KEY,
    tenant_id  INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    ticket_id  INTEGER NOT NULL REFERENCES tickets(id) ON DELETE CASCADE,
    ci_id      INTEGER NOT NULL REFERENCES configuration_items(id) ON DELETE CASCADE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (ticket_id, ci_id)
);
CREATE INDEX IF NOT EXISTS idx_tcl_ticket ON ticket_ci_links (ticket_id);
CREATE INDEX IF NOT EXISTS idx_tcl_ci ON ticket_ci_links (ci_id);

-- ── Wissensdatenbank: Freigabe-Workflow (Knowledge Management) ────────────────
-- Bestehende Artikel werden einmalig 'Freigegeben'; neue starten als 'Entwurf'.
ALTER TABLE kb_articles ADD COLUMN IF NOT EXISTS status TEXT;
UPDATE kb_articles SET status='Freigegeben' WHERE status IS NULL;
ALTER TABLE kb_articles ALTER COLUMN status SET DEFAULT 'Entwurf';
ALTER TABLE kb_articles ALTER COLUMN status SET NOT NULL;

-- ── Login-Rate-Limiting (Brute-Force-Schutz, DB-basiert => multi-worker-fest) ──
CREATE TABLE IF NOT EXISTS login_attempts (
    id      SERIAL PRIMARY KEY,
    email   TEXT NOT NULL,
    ip      TEXT,
    zeit    TIMESTAMPTZ NOT NULL DEFAULT now(),
    success BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_login_attempts_email_zeit ON login_attempts (email, zeit DESC);
CREATE INDEX IF NOT EXISTS idx_login_attempts_ip_zeit ON login_attempts (ip, zeit DESC);

-- ── Server-seitige Sessions (Rust-Rewrite 2026-07-15) ─────────────────────────
-- In der DB liegt nur der SHA-256-Hash des Cookie-Tokens: ein DB-Leak
-- kompromittiert keine laufenden Sessions; Sessions sind widerrufbar.
CREATE TABLE IF NOT EXISTS sessions (
    token_hash TEXT PRIMARY KEY,
    user_id    INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    csrf_token TEXT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    expires_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions (user_id);
CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions (expires_at);
