-- ═══════════════════════════════════════════════════════════════════════════
-- AAMOS Billing Engine — PostgreSQL Schema
-- LandveX Enterprise Platform — Faktureringsmodul
-- Principer: Audit First · Tenant Isolation · Immutable Journal
-- ═══════════════════════════════════════════════════════════════════════════

-- ── Agreements (Avtal) ──────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS billing_agreements (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,

  agreement_number    TEXT NOT NULL,
  agreement_type      TEXT NOT NULL,           -- 'kommunalt'|'kommersiellt'|'internt'
  status              TEXT NOT NULL DEFAULT 'draft', -- 'draft'|'active'|'suspended'|'terminated'|'expired'

  customer_type       TEXT NOT NULL,           -- 'municipality'|'company'|'individual'
  customer_org_nr     TEXT,
  customer_name       TEXT NOT NULL,
  customer_address    TEXT,
  customer_postal     TEXT,
  customer_city       TEXT,
  customer_contact    TEXT,
  customer_email      TEXT,
  customer_phone      TEXT,
  customer_reference  TEXT,

  title               TEXT NOT NULL,
  description         TEXT,
  start_date          DATE NOT NULL,
  end_date            DATE,
  auto_renew          BOOLEAN NOT NULL DEFAULT FALSE,
  renewal_period_months INTEGER,
  termination_notice_days INTEGER DEFAULT 90,

  currency            TEXT NOT NULL DEFAULT 'SEK',
  billing_frequency   TEXT NOT NULL,           -- 'monthly'|'quarterly'|'semi_annual'|'annual'|'one_time'
  billing_day         INTEGER NOT NULL DEFAULT 1,
  payment_terms_days  INTEGER NOT NULL DEFAULT 30,
  late_fee_percent    NUMERIC(5,2) DEFAULT 8.0,
  vat_rate            NUMERIC(5,2) NOT NULL DEFAULT 25.0,

  revenue_account     TEXT NOT NULL DEFAULT '3000',
  receivable_account  TEXT NOT NULL DEFAULT '1510',
  vat_account         TEXT NOT NULL DEFAULT '2610',

  external_system_id  TEXT,
  external_system     TEXT,                    -- 'visma'|'agresso'|'fortnox'|'custom'

  metadata            JSONB NOT NULL DEFAULT '{}',
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  created_by          TEXT NOT NULL,
  updated_by          TEXT NOT NULL,
  trace_id            TEXT NOT NULL,

  UNIQUE (tenant_id, agreement_number),
  CONSTRAINT chk_agreement_status CHECK (status IN ('draft','active','suspended','terminated','expired')),
  CONSTRAINT chk_agreement_type   CHECK (agreement_type IN ('kommunalt','kommersiellt','internt')),
  CONSTRAINT chk_customer_type    CHECK (customer_type IN ('municipality','company','individual')),
  CONSTRAINT chk_billing_freq     CHECK (billing_frequency IN ('monthly','quarterly','semi_annual','annual','one_time'))
);

CREATE INDEX IF NOT EXISTS idx_agreements_tenant_status ON billing_agreements (tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_agreements_customer       ON billing_agreements (tenant_id, customer_org_nr);
CREATE INDEX IF NOT EXISTS idx_agreements_dates          ON billing_agreements (tenant_id, start_date, end_date);

-- ── Agreement Lines (Avtalsrader) ───────────────────────────────────────────
CREATE TABLE IF NOT EXISTS billing_agreement_lines (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  agreement_id        UUID NOT NULL REFERENCES billing_agreements(id) ON DELETE CASCADE,
  tenant_id           TEXT NOT NULL,

  line_number         INTEGER NOT NULL,
  description         TEXT NOT NULL,
  quantity            NUMERIC(12,2) NOT NULL DEFAULT 1,
  unit                TEXT,                    -- 'st'|'tim'|'mån'|'år'|'m²'
  unit_price          NUMERIC(18,2) NOT NULL,
  vat_rate            NUMERIC(5,2),
  discount_percent    NUMERIC(5,2) DEFAULT 0,
  discount_amount     NUMERIC(18,2),

  revenue_account     TEXT,
  cost_center         TEXT,
  project_code        TEXT,

  active_from         DATE NOT NULL DEFAULT CURRENT_DATE,
  active_until        DATE,
  is_active           BOOLEAN NOT NULL DEFAULT TRUE,

  metadata            JSONB NOT NULL DEFAULT '{}',
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),

  UNIQUE (agreement_id, line_number)
);

CREATE INDEX IF NOT EXISTS idx_agreement_lines_agreement ON billing_agreement_lines (agreement_id);
CREATE INDEX IF NOT EXISTS idx_agreement_lines_active    ON billing_agreement_lines (agreement_id, is_active, active_from, active_until);

-- ── Invoices (Fakturor) ─────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS billing_invoices (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,

  invoice_number      TEXT NOT NULL,
  agreement_id        UUID REFERENCES billing_agreements(id),
  invoice_type        TEXT NOT NULL DEFAULT 'standard', -- 'standard'|'credit'|'proforma'|'reminder'
  status              TEXT NOT NULL DEFAULT 'draft', -- 'draft'|'sent'|'paid'|'partially_paid'|'overdue'|'cancelled'|'credited'

  customer_org_nr     TEXT,
  customer_name       TEXT NOT NULL,
  customer_address    TEXT,
  customer_postal     TEXT,
  customer_city       TEXT,
  customer_contact    TEXT,
  customer_email      TEXT,
  customer_reference  TEXT,

  invoice_date        DATE NOT NULL,
  due_date            DATE NOT NULL,
  delivery_date       DATE,
  sent_at             TIMESTAMPTZ,
  paid_at             TIMESTAMPTZ,
  credited_at         TIMESTAMPTZ,

  currency            TEXT NOT NULL DEFAULT 'SEK',
  subtotal            NUMERIC(18,2) NOT NULL DEFAULT 0,
  vat_total           NUMERIC(18,2) NOT NULL DEFAULT 0,
  total               NUMERIC(18,2) NOT NULL DEFAULT 0,
  amount_paid         NUMERIC(18,2) NOT NULL DEFAULT 0,
  amount_due          NUMERIC(18,2) NOT NULL DEFAULT 0,

  vat_specification   JSONB NOT NULL DEFAULT '[]',

  journal_entry_id    UUID,
  ledger_posted_at    TIMESTAMPTZ,

  credited_by_invoice_id UUID,
  original_invoice_id    UUID,

  reminder_count      INTEGER NOT NULL DEFAULT 0,
  last_reminder_at    TIMESTAMPTZ,
  reminder_fee        NUMERIC(18,2) DEFAULT 0,
  interest_fee        NUMERIC(18,2) DEFAULT 0,

  pdf_generated_at    TIMESTAMPTZ,
  pdf_url             TEXT,

  external_system_id  TEXT,
  external_system     TEXT,
  exported_at         TIMESTAMPTZ,

  metadata            JSONB NOT NULL DEFAULT '{}',
  notes               TEXT,
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  created_by          TEXT NOT NULL,
  updated_by          TEXT NOT NULL,

  trace_id            TEXT NOT NULL,
  correlation_id      TEXT NOT NULL,

  UNIQUE (tenant_id, invoice_number),
  CONSTRAINT chk_invoice_status CHECK (status IN ('draft','sent','paid','partially_paid','overdue','cancelled','credited')),
  CONSTRAINT chk_invoice_type   CHECK (invoice_type IN ('standard','credit','proforma','reminder'))
);

CREATE INDEX IF NOT EXISTS idx_invoices_tenant_status ON billing_invoices (tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_invoices_agreement     ON billing_invoices (agreement_id);
CREATE INDEX IF NOT EXISTS idx_invoices_dates         ON billing_invoices (tenant_id, invoice_date, due_date);
CREATE INDEX IF NOT EXISTS idx_invoices_overdue       ON billing_invoices (tenant_id, status, due_date) WHERE status IN ('sent', 'partially_paid');
CREATE INDEX IF NOT EXISTS idx_invoices_journal       ON billing_invoices (journal_entry_id);
CREATE INDEX IF NOT EXISTS idx_invoices_external      ON billing_invoices (tenant_id, external_system, external_system_id);

-- ── Invoice Lines (Fakturarader) ────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS billing_invoice_lines (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  invoice_id          UUID NOT NULL REFERENCES billing_invoices(id) ON DELETE CASCADE,
  tenant_id           TEXT NOT NULL,

  line_number         INTEGER NOT NULL,
  description         TEXT NOT NULL,
  quantity            NUMERIC(12,2) NOT NULL DEFAULT 1,
  unit                TEXT,
  unit_price          NUMERIC(18,2) NOT NULL,
  line_total          NUMERIC(18,2) NOT NULL,

  vat_rate            NUMERIC(5,2) NOT NULL,
  vat_amount          NUMERIC(18,2) NOT NULL,
  line_total_incl_vat NUMERIC(18,2) NOT NULL,

  agreement_line_id   UUID REFERENCES billing_agreement_lines(id),

  revenue_account     TEXT NOT NULL,
  cost_center         TEXT,
  project_code        TEXT,

  metadata            JSONB NOT NULL DEFAULT '{}',

  UNIQUE (invoice_id, line_number)
);

CREATE INDEX IF NOT EXISTS idx_invoice_lines_invoice ON billing_invoice_lines (invoice_id);
CREATE INDEX IF NOT EXISTS idx_invoice_lines_account ON billing_invoice_lines (tenant_id, revenue_account);

-- ── Payments (Inbetalningar) ────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS billing_payments (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,

  invoice_id          UUID NOT NULL REFERENCES billing_invoices(id),
  payment_date        DATE NOT NULL,
  amount              NUMERIC(18,2) NOT NULL,
  currency            TEXT NOT NULL DEFAULT 'SEK',

  payment_method      TEXT NOT NULL,           -- 'bank_transfer'|'swish'|'card'|'cash'|'autogiro'|'other'
  payment_reference   TEXT,
  bank_transaction_id TEXT,

  reconciliation_status TEXT NOT NULL DEFAULT 'unreconciled', -- 'unreconciled'|'matched'|'reconciled'
  reconciled_at       TIMESTAMPTZ,
  reconciled_by       TEXT,

  journal_entry_id    UUID,

  metadata            JSONB NOT NULL DEFAULT '{}',
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  created_by          TEXT NOT NULL,
  trace_id            TEXT NOT NULL,

  UNIQUE (tenant_id, bank_transaction_id),
  CONSTRAINT chk_payment_method CHECK (payment_method IN ('bank_transfer','swish','card','cash','autogiro','other')),
  CONSTRAINT chk_recon_status   CHECK (reconciliation_status IN ('unreconciled','matched','reconciled'))
);

CREATE INDEX IF NOT EXISTS idx_payments_invoice        ON billing_payments (invoice_id);
CREATE INDEX IF NOT EXISTS idx_payments_reconciliation ON billing_payments (tenant_id, reconciliation_status);
CREATE INDEX IF NOT EXISTS idx_payments_date           ON billing_payments (tenant_id, payment_date);

-- ── Reminders (Påminnelser / Inkasso) ───────────────────────────────────────
CREATE TABLE IF NOT EXISTS billing_reminders (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,
  invoice_id          UUID NOT NULL REFERENCES billing_invoices(id),

  reminder_number     INTEGER NOT NULL,
  reminder_type       TEXT NOT NULL,           -- 'reminder'|'debt_collection'|'legal'
  sent_at             TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  due_date            DATE NOT NULL,

  reminder_fee        NUMERIC(18,2) NOT NULL DEFAULT 0,
  interest_fee        NUMERIC(18,2) NOT NULL DEFAULT 0,
  total_demand        NUMERIC(18,2) NOT NULL,

  status              TEXT NOT NULL DEFAULT 'sent', -- 'sent'|'paid'|'escalated'|'cancelled'

  sent_via            TEXT,                    -- 'email'|'post'|'sms'
  email_address       TEXT,
  pdf_url             TEXT,

  metadata            JSONB NOT NULL DEFAULT '{}',
  created_by          TEXT NOT NULL,
  trace_id            TEXT NOT NULL,

  CONSTRAINT chk_reminder_type CHECK (reminder_type IN ('reminder','debt_collection','legal')),
  CONSTRAINT chk_reminder_status CHECK (status IN ('sent','paid','escalated','cancelled'))
);

CREATE INDEX IF NOT EXISTS idx_reminders_invoice ON billing_reminders (invoice_id);
CREATE INDEX IF NOT EXISTS idx_reminders_status  ON billing_reminders (tenant_id, status);

-- ── Schedules (Schemaläggning) ──────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS billing_schedules (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,
  agreement_id        UUID NOT NULL REFERENCES billing_agreements(id) ON DELETE CASCADE,

  schedule_type       TEXT NOT NULL,           -- 'recurring'|'one_time'
  frequency           TEXT NOT NULL,           -- 'monthly'|'quarterly'|'semi_annual'|'annual'
  day_of_month        INTEGER NOT NULL DEFAULT 1,
  next_run_date       DATE NOT NULL,
  last_run_date       DATE,
  last_invoice_id     UUID REFERENCES billing_invoices(id),

  is_active           BOOLEAN NOT NULL DEFAULT TRUE,
  paused_until        DATE,

  runs_count          INTEGER NOT NULL DEFAULT 0,
  failures_count      INTEGER NOT NULL DEFAULT 0,
  last_error          TEXT,

  metadata            JSONB NOT NULL DEFAULT '{}',
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),

  UNIQUE (tenant_id, agreement_id, schedule_type),
  CONSTRAINT chk_schedule_type CHECK (schedule_type IN ('recurring','one_time')),
  CONSTRAINT chk_schedule_freq CHECK (frequency IN ('monthly','quarterly','semi_annual','annual'))
);

CREATE INDEX IF NOT EXISTS idx_schedules_next_run ON billing_schedules (next_run_date, is_active) WHERE is_active = TRUE;

-- ── Audit Log ───────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS billing_audit_log (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,
  trace_id            TEXT NOT NULL,
  correlation_id      TEXT NOT NULL,
  user_id             TEXT NOT NULL,
  entity_type         TEXT NOT NULL,           -- 'agreement'|'invoice'|'payment'|'reminder'
  entity_id           TEXT NOT NULL,
  action              TEXT NOT NULL,           -- 'created'|'updated'|'sent'|'paid'|'cancelled'|'credited'|'reminded'
  decision_source     TEXT NOT NULL DEFAULT 'user',
  before_state        JSONB,
  after_state         JSONB,
  ip_address          TEXT,
  session_id          TEXT,
  ts                  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_billing_audit_entity ON billing_audit_log (tenant_id, entity_type, entity_id);
CREATE INDEX IF NOT EXISTS idx_billing_audit_trace  ON billing_audit_log (trace_id);
CREATE INDEX IF NOT EXISTS idx_billing_audit_ts     ON billing_audit_log (tenant_id, ts);

-- ── Export Jobs ─────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS billing_export_jobs (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,

  job_type            TEXT NOT NULL,           -- 'sie4'|'pdf'|'csv'|'visma'|'agresso'
  job_status          TEXT NOT NULL DEFAULT 'pending', -- 'pending'|'running'|'completed'|'failed'

  period_from         TEXT,
  period_to           TEXT,
  fiscal_year         INTEGER,
  invoice_ids         UUID[],
  agreement_ids       UUID[],

  file_url            TEXT,
  file_size_bytes     INTEGER,
  record_count        INTEGER,
  error_message       TEXT,

  requested_by        TEXT NOT NULL,
  started_at          TIMESTAMPTZ,
  completed_at        TIMESTAMPTZ,
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  trace_id            TEXT NOT NULL,

  CONSTRAINT chk_export_type   CHECK (job_type IN ('sie4','pdf','csv','visma','agresso')),
  CONSTRAINT chk_export_status CHECK (job_status IN ('pending','running','completed','failed'))
);

CREATE INDEX IF NOT EXISTS idx_export_jobs_tenant ON billing_export_jobs (tenant_id, job_status);

-- ── Integration Configs ─────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS billing_integration_configs (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id           TEXT NOT NULL,
  system_name         TEXT NOT NULL,           -- 'visma'|'agresso'|'fortnox'|'custom'
  is_active           BOOLEAN NOT NULL DEFAULT FALSE,
  config              JSONB NOT NULL,
  last_sync_at        TIMESTAMPTZ,
  last_sync_status    TEXT,
  last_error          TEXT,
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),

  UNIQUE (tenant_id, system_name),
  CONSTRAINT chk_integration_system CHECK (system_name IN ('visma','agresso','fortnox','custom'))
);
