<?php

declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';

/**
 * @return list<string>
 */
function ledger_normalize_email_norms(array $emails): array
{
    $normalized = [];
    foreach ($emails as $email) {
        $emailNorm = strtolower(trim((string)$email));
        if ($emailNorm !== '') {
            $normalized[$emailNorm] = true;
        }
    }

    return array_keys($normalized);
}

/**
 * @return list<int>
 */
function ledger_normalize_ids(array $ids): array
{
    $normalized = [];
    foreach ($ids as $id) {
        $id = (int)$id;
        if ($id > 0) {
            $normalized[$id] = true;
        }
    }

    return array_keys($normalized);
}

function ledger_current_year(): int
{
    return (int)date('Y');
}

function ledger_default_tenant_slug(): string
{
    $tenantSlug = app_env('M4_DEFAULT_TENANT_SLUG');
    if ($tenantSlug === null || trim($tenantSlug) === '') {
        $tenantSlug = app_env('M3_DEFAULT_TENANT_SLUG', 'default');
    }

    return trim((string)$tenantSlug) !== '' ? trim((string)$tenantSlug) : 'default';
}

/**
 * @return array{id: int, name: string, slug: string}|null
 */
function ledger_fetch_tenant_by_slug(PDO $pdo, string $slug): ?array
{
    $stmt = $pdo->prepare('SELECT id, name, slug FROM tenants WHERE slug = ? LIMIT 1');
    $stmt->execute([trim($slug)]);
    $tenant = $stmt->fetch();

    if ($tenant === false) {
        return null;
    }

    return [
        'id' => (int)$tenant['id'],
        'name' => (string)$tenant['name'],
        'slug' => (string)$tenant['slug'],
    ];
}

/**
 * @return array{id: int, name: string, slug: string}|null
 */
function ledger_fetch_default_tenant(PDO $pdo): ?array
{
    return ledger_fetch_tenant_by_slug($pdo, ledger_default_tenant_slug());
}

/**
 * @return array{total_payments_cents: int, total_costs_cents: int, total_marks: int, balance_cents: int, year_payments_cents: int, year_costs_cents: int, year_marks: int}
 */
function ledger_empty_totals(): array
{
    return [
        'total_payments_cents' => 0,
        'total_costs_cents' => 0,
        'total_marks' => 0,
        'balance_cents' => 0,
        'year_payments_cents' => 0,
        'year_costs_cents' => 0,
        'year_marks' => 0,
    ];
}

/**
 * @return array{total_payments_cents: int, total_costs_cents: int, total_marks: int, balance_cents: int, year_payments_cents: int, year_costs_cents: int, year_marks: int}
 */
function ledger_fetch_tenant_totals(PDO $pdo, int $tenantId, ?int $year = null): array
{
    $year = $year ?? ledger_current_year();
    $stmt = $pdo->prepare(
        "SELECT
            COALESCE(SUM(CASE WHEN type = 'payment' THEN amount_cents ELSE 0 END), 0) AS total_payments_cents,
            COALESCE(SUM(CASE WHEN type = 'consumption' THEN -amount_cents ELSE 0 END), 0) AS total_costs_cents,
            COALESCE(SUM(CASE WHEN type = 'consumption' THEN marks_count ELSE 0 END), 0) AS total_marks,
            COALESCE(SUM(amount_cents), 0) AS balance_cents,
            COALESCE(SUM(CASE WHEN type = 'payment' AND YEAR(booked_at) = ? THEN amount_cents ELSE 0 END), 0) AS year_payments_cents,
            COALESCE(SUM(CASE WHEN type = 'consumption' AND YEAR(booked_at) = ? THEN -amount_cents ELSE 0 END), 0) AS year_costs_cents,
            COALESCE(SUM(CASE WHEN type = 'consumption' AND YEAR(booked_at) = ? THEN marks_count ELSE 0 END), 0) AS year_marks
         FROM ledger_entries
         WHERE tenant_id = ?
           AND voided_at IS NULL"
    );
    $stmt->execute([$year, $year, $year, $tenantId]);
    $row = $stmt->fetch();

    if ($row === false) {
        return ledger_empty_totals();
    }

    return [
        'total_payments_cents' => (int)$row['total_payments_cents'],
        'total_costs_cents' => (int)$row['total_costs_cents'],
        'total_marks' => (int)$row['total_marks'],
        'balance_cents' => (int)$row['balance_cents'],
        'year_payments_cents' => (int)$row['year_payments_cents'],
        'year_costs_cents' => (int)$row['year_costs_cents'],
        'year_marks' => (int)$row['year_marks'],
    ];
}

/**
 * Options:
 * - year: int
 * - active_only: bool|null
 * - participant_ids: list<int>
 * - legacy_mitarbeiter_ids: list<int>
 * - email_norms: list<string>
 *
 * @return list<array{participant_id: int, tenant_id: int, legacy_mitarbeiter_id: ?int, display_name: string, email: ?string, email_norm: ?string, paypal_name: ?string, active: bool, total_payments_cents: int, total_costs_cents: int, total_marks: int, balance_cents: int, year_payments_cents: int, year_costs_cents: int, year_marks: int}>
 */
function ledger_fetch_participant_summaries(PDO $pdo, int $tenantId, array $options = []): array
{
    $year = isset($options['year']) ? (int)$options['year'] : ledger_current_year();
    $where = ['p.tenant_id = ?'];
    $params = [$year, $year, $year, $tenantId];

    if (array_key_exists('active_only', $options) && $options['active_only'] !== null) {
        $where[] = 'p.active = ?';
        $params[] = $options['active_only'] ? 1 : 0;
    }

    if (array_key_exists('participant_ids', $options)) {
        $participantIds = is_array($options['participant_ids'])
            ? ledger_normalize_ids($options['participant_ids'])
            : [];
        if ($participantIds === []) {
            return [];
        }
        $where[] = 'p.id IN (' . implode(',', array_fill(0, count($participantIds), '?')) . ')';
        array_push($params, ...$participantIds);
    }

    if (array_key_exists('legacy_mitarbeiter_ids', $options)) {
        $legacyIds = is_array($options['legacy_mitarbeiter_ids'])
            ? ledger_normalize_ids($options['legacy_mitarbeiter_ids'])
            : [];
        if ($legacyIds === []) {
            return [];
        }
        $where[] = 'p.legacy_mitarbeiter_id IN (' . implode(',', array_fill(0, count($legacyIds), '?')) . ')';
        array_push($params, ...$legacyIds);
    }

    if (array_key_exists('email_norms', $options)) {
        $emailNorms = is_array($options['email_norms'])
            ? ledger_normalize_email_norms($options['email_norms'])
            : [];
        if ($emailNorms === []) {
            return [];
        }
        $where[] = 'p.email_norm IN (' . implode(',', array_fill(0, count($emailNorms), '?')) . ')';
        array_push($params, ...$emailNorms);
    }

    $sql = "
        SELECT
            p.id AS participant_id,
            p.tenant_id,
            p.legacy_mitarbeiter_id,
            p.display_name,
            p.email,
            p.email_norm,
            p.paypal_name,
            p.active,
            COALESCE(SUM(CASE WHEN le.type = 'payment' THEN le.amount_cents ELSE 0 END), 0) AS total_payments_cents,
            COALESCE(SUM(CASE WHEN le.type = 'consumption' THEN -le.amount_cents ELSE 0 END), 0) AS total_costs_cents,
            COALESCE(SUM(CASE WHEN le.type = 'consumption' THEN le.marks_count ELSE 0 END), 0) AS total_marks,
            COALESCE(SUM(le.amount_cents), 0) AS balance_cents,
            COALESCE(SUM(CASE WHEN le.type = 'payment' AND YEAR(le.booked_at) = ? THEN le.amount_cents ELSE 0 END), 0) AS year_payments_cents,
            COALESCE(SUM(CASE WHEN le.type = 'consumption' AND YEAR(le.booked_at) = ? THEN -le.amount_cents ELSE 0 END), 0) AS year_costs_cents,
            COALESCE(SUM(CASE WHEN le.type = 'consumption' AND YEAR(le.booked_at) = ? THEN le.marks_count ELSE 0 END), 0) AS year_marks
        FROM participants p
        LEFT JOIN ledger_entries le
          ON le.tenant_id = p.tenant_id
         AND le.participant_id = p.id
         AND le.voided_at IS NULL
        WHERE " . implode(' AND ', $where) . "
        GROUP BY p.id, p.tenant_id, p.legacy_mitarbeiter_id, p.display_name, p.email, p.email_norm, p.paypal_name, p.active
        ORDER BY p.display_name, p.id";

    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);

    $summaries = [];
    while ($row = $stmt->fetch()) {
        $summaries[] = [
            'participant_id' => (int)$row['participant_id'],
            'tenant_id' => (int)$row['tenant_id'],
            'legacy_mitarbeiter_id' => $row['legacy_mitarbeiter_id'] !== null ? (int)$row['legacy_mitarbeiter_id'] : null,
            'display_name' => (string)$row['display_name'],
            'email' => $row['email'] !== null ? (string)$row['email'] : null,
            'email_norm' => $row['email_norm'] !== null ? (string)$row['email_norm'] : null,
            'paypal_name' => $row['paypal_name'] !== null ? (string)$row['paypal_name'] : null,
            'active' => (int)$row['active'] === 1,
            'total_payments_cents' => (int)$row['total_payments_cents'],
            'total_costs_cents' => (int)$row['total_costs_cents'],
            'total_marks' => (int)$row['total_marks'],
            'balance_cents' => (int)$row['balance_cents'],
            'year_payments_cents' => (int)$row['year_payments_cents'],
            'year_costs_cents' => (int)$row['year_costs_cents'],
            'year_marks' => (int)$row['year_marks'],
        ];
    }

    return $summaries;
}

/**
 * @return array{participant_id: int, tenant_id: int, legacy_mitarbeiter_id: ?int, display_name: string, email: ?string, email_norm: ?string, paypal_name: ?string, active: bool, total_payments_cents: int, total_costs_cents: int, total_marks: int, balance_cents: int, year_payments_cents: int, year_costs_cents: int, year_marks: int}|null
 */
function ledger_fetch_participant_summary(PDO $pdo, int $tenantId, int $participantId, ?int $year = null): ?array
{
    $summaries = ledger_fetch_participant_summaries($pdo, $tenantId, [
        'participant_ids' => [$participantId],
        'year' => $year ?? ledger_current_year(),
    ]);

    return $summaries[0] ?? null;
}

/**
 * @return array{participant_id: int, tenant_id: int, legacy_mitarbeiter_id: ?int, display_name: string, email: ?string, email_norm: ?string, paypal_name: ?string, active: bool, total_payments_cents: int, total_costs_cents: int, total_marks: int, balance_cents: int, year_payments_cents: int, year_costs_cents: int, year_marks: int}|null
 */
function ledger_fetch_participant_summary_by_legacy_id(PDO $pdo, int $tenantId, int $legacyMitarbeiterId, ?int $year = null): ?array
{
    $summaries = ledger_fetch_participant_summaries($pdo, $tenantId, [
        'legacy_mitarbeiter_ids' => [$legacyMitarbeiterId],
        'year' => $year ?? ledger_current_year(),
    ]);

    return $summaries[0] ?? null;
}

function ledger_mirror_legacy_consumption(PDO $pdo, int $tenantId, int $legacyConsumptionId): void
{
    $stmt = $pdo->prepare(
        "INSERT INTO ledger_entries
           (tenant_id, participant_id, type, amount_cents, marks_count,
            unit_price_cents, booked_at, source, note, legacy_table, legacy_id)
         SELECT
           p.tenant_id,
           p.id,
           'consumption',
           -CAST(ROUND(v.Kosten * 100) AS SIGNED),
           v.AnzahlStriche,
           CAST(ROUND(v.KostenproStrich * 100) AS SIGNED),
           v.Datum,
           CASE
             WHEN v.Eintragsart = 2 THEN 'legacy_web'
             ELSE 'legacy_manual'
           END,
           CASE
             WHEN v.Eintragsart IS NULL THEN NULL
             ELSE CONCAT('Legacy Eintragsart: ', v.Eintragsart)
           END,
           'kl_Kaffeeverbrauch',
           v.VerbrauchID
         FROM kl_Kaffeeverbrauch v
         JOIN participants p
           ON p.tenant_id = ?
          AND p.legacy_mitarbeiter_id = v.MitarbeiterID
         WHERE v.VerbrauchID = ?
         ON DUPLICATE KEY UPDATE
           participant_id = VALUES(participant_id),
           type = VALUES(type),
           amount_cents = VALUES(amount_cents),
           marks_count = VALUES(marks_count),
           unit_price_cents = VALUES(unit_price_cents),
           booked_at = VALUES(booked_at),
           source = VALUES(source),
           note = VALUES(note)"
    );
    $stmt->execute([$tenantId, $legacyConsumptionId]);

    $check = $pdo->prepare(
        "SELECT COUNT(*)
         FROM ledger_entries
         WHERE tenant_id = ?
           AND legacy_table = 'kl_Kaffeeverbrauch'
           AND legacy_id = ?"
    );
    $check->execute([$tenantId, $legacyConsumptionId]);
    if ((int)$check->fetchColumn() !== 1) {
        throw new RuntimeException('Der Legacy-Strich konnte nicht ins Ledger gespiegelt werden.');
    }
}

/**
 * Options:
 * - participant_id: int
 * - type: string
 * - include_voided: bool
 * - limit: int
 *
 * @return list<array{id: int, tenant_id: int, participant_id: int, display_name: string, type: string, amount_cents: int, marks_count: ?int, unit_price_cents: ?int, booked_at: string, source: string, note: ?string, legacy_table: ?string, legacy_id: ?int, voided_at: ?string}>
 */
function ledger_fetch_recent_entries(PDO $pdo, int $tenantId, array $options = []): array
{
    $limit = isset($options['limit']) ? (int)$options['limit'] : 100;
    $limit = max(1, min($limit, 500));
    $where = ['le.tenant_id = ?'];
    $params = [$tenantId];

    if (empty($options['include_voided'])) {
        $where[] = 'le.voided_at IS NULL';
    }

    if (!empty($options['participant_id'])) {
        $where[] = 'le.participant_id = ?';
        $params[] = (int)$options['participant_id'];
    }

    if (!empty($options['type'])) {
        $where[] = 'le.type = ?';
        $params[] = trim((string)$options['type']);
    }

    $sql = "
        SELECT
            le.id,
            le.tenant_id,
            le.participant_id,
            p.display_name,
            le.type,
            le.amount_cents,
            le.marks_count,
            le.unit_price_cents,
            le.booked_at,
            le.source,
            le.note,
            le.legacy_table,
            le.legacy_id,
            le.voided_at
        FROM ledger_entries le
        JOIN participants p
          ON p.tenant_id = le.tenant_id
         AND p.id = le.participant_id
        WHERE " . implode(' AND ', $where) . "
        ORDER BY le.booked_at DESC, le.id DESC
        LIMIT {$limit}";

    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);

    $entries = [];
    while ($row = $stmt->fetch()) {
        $entries[] = [
            'id' => (int)$row['id'],
            'tenant_id' => (int)$row['tenant_id'],
            'participant_id' => (int)$row['participant_id'],
            'display_name' => (string)$row['display_name'],
            'type' => (string)$row['type'],
            'amount_cents' => (int)$row['amount_cents'],
            'marks_count' => $row['marks_count'] !== null ? (int)$row['marks_count'] : null,
            'unit_price_cents' => $row['unit_price_cents'] !== null ? (int)$row['unit_price_cents'] : null,
            'booked_at' => (string)$row['booked_at'],
            'source' => (string)$row['source'],
            'note' => $row['note'] !== null ? (string)$row['note'] : null,
            'legacy_table' => $row['legacy_table'] !== null ? (string)$row['legacy_table'] : null,
            'legacy_id' => $row['legacy_id'] !== null ? (int)$row['legacy_id'] : null,
            'voided_at' => $row['voided_at'] !== null ? (string)$row['voided_at'] : null,
        ];
    }

    return $entries;
}
