<?php

declare(strict_types=1);

require_once __DIR__ . '/contract_admin.php';

function local_store_path(): string
{
    return app_config()['local_db'] ?? (__DIR__ . '/../database/data.json');
}

function local_store_read(): array
{
    $path = local_store_path();

    if (!is_file($path)) {
        return [
            'contracts' => [],
            'signatures' => [],
            'counters' => ['contracts' => 0, 'signatures' => 0],
        ];
    }

    $raw = file_get_contents($path);
    $data = json_decode($raw ?: '', true);

    if (!is_array($data)) {
        return [
            'contracts' => [],
            'signatures' => [],
            'counters' => ['contracts' => 0, 'signatures' => 0],
        ];
    }

    $data['contracts'] = is_array($data['contracts'] ?? null) ? $data['contracts'] : [];
    $data['signatures'] = is_array($data['signatures'] ?? null) ? $data['signatures'] : [];
    $data['counters'] = is_array($data['counters'] ?? null) ? $data['counters'] : ['contracts' => 0, 'signatures' => 0];

    return $data;
}

function local_store_write(array $data): void
{
    $path = local_store_path();
    $dir = dirname($path);

    if (!is_dir($dir)) {
        if (!@mkdir($dir, 0755, true) && !is_dir($dir)) {
            throw new RuntimeException('Cannot create database directory.');
        }
    }

    $encoded = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    if ($encoded === false) {
        throw new RuntimeException('Cannot encode contract data.');
    }

    $written = @file_put_contents($path, $encoded, LOCK_EX);
    if ($written === false) {
        throw new RuntimeException('Cannot write to database file. Check folder permissions for database/.');
    }
}

function local_store_next_id(array &$data, string $counterKey): int
{
    $current = (int)($data['counters'][$counterKey] ?? 0);
    $current++;
    $data['counters'][$counterKey] = $current;

    return $current;
}

function local_store_all_contracts(): array
{
    $data = local_store_read();

    return array_values(array_reverse($data['contracts']));
}

function local_store_resolve_token(array $contract, ?string $fallback = null): string
{
    return trim((string)($contract['token'] ?? $contract['sign_token'] ?? $fallback ?? ''));
}

function local_store_contract_matches(array $contract, string $token): bool
{
    $token = trim($token);
    if ($token === '') {
        return false;
    }

    foreach (['token', 'sign_token'] as $key) {
        if (trim((string)($contract[$key] ?? '')) === $token) {
            return true;
        }
    }

    return false;
}

function local_store_find(string $token): ?array
{
    $token = trim($token);
    if ($token === '') {
        return null;
    }

    foreach (local_store_read()['contracts'] as $contract) {
        if (local_store_contract_matches($contract, $token)) {
            return $contract;
        }
    }

    return null;
}

function local_store_find_by_id(int $id): ?array
{
    if ($id <= 0) {
        return null;
    }

    foreach (local_store_read()['contracts'] as $contract) {
        if ((int)($contract['id'] ?? 0) === $id) {
            return $contract;
        }
    }

    return null;
}

function local_store_upsert(array $contract): array
{
    $token = local_store_resolve_token($contract);
    if ($token === '') {
        throw new InvalidArgumentException('Contract token is required.');
    }

    $contract['token'] = $contract['token'] ?? $token;
    $contract['sign_token'] = $contract['sign_token'] ?? $token;

    $existing = local_store_find($token);
    if ($existing !== null) {
        $updated = local_store_update($token, array_merge($existing, $contract));

        return $updated ?? array_merge($existing, $contract);
    }

    return local_store_save($contract);
}

function local_store_save(array $contract): array
{
    $data = local_store_read();

    if (empty($contract['id'])) {
        $contract['id'] = local_store_next_id($data, 'contracts');
    }

    if (empty($contract['created_at'])) {
        $contract['created_at'] = date('Y-m-d H:i:s');
    }

    if (empty($contract['status'])) {
        $contract['status'] = 'pending';
    }

    $data['contracts'][] = $contract;
    local_store_write($data);

    return $contract;
}

function local_store_update(string $token, array $updates): ?array
{
    $data = local_store_read();
    $updated = null;

    foreach ($data['contracts'] as $index => $contract) {
        if (!local_store_contract_matches($contract, $token)) {
            continue;
        }

        $data['contracts'][$index] = array_merge($contract, $updates);
        $updated = $data['contracts'][$index];
        break;
    }

    if ($updated === null) {
        return null;
    }

    local_store_write($data);

    return $updated;
}

function local_store_delete_contract(string $token): bool
{
    $data = local_store_read();
    $contract = null;
    $contractIndex = null;

    foreach ($data['contracts'] as $index => $item) {
        if (local_store_contract_matches($item, $token)) {
            $contract = $item;
            $contractIndex = $index;
            break;
        }
    }

    if ($contract === null || $contractIndex === null) {
        return false;
    }

    $signatures = array_values(array_filter(
        $data['signatures'],
        static fn(array $item): bool => ($item['contract_token'] ?? '') === $token
    ));

    unset($data['contracts'][$contractIndex]);
    $data['contracts'] = array_values($data['contracts']);
    $data['signatures'] = array_values(array_filter(
        $data['signatures'],
        static fn(array $item): bool => ($item['contract_token'] ?? '') !== $token
    ));

    local_store_write($data);
    contract_delete_related_files($token, $contract, $signatures);

    return true;
}

function local_store_duplicate_contract(string $token): ?array
{
    $source = local_store_find($token);
    if ($source === null) {
        return null;
    }

    return local_store_save(contract_prepare_duplicate($source));
}

function local_store_owner_signatures(string $token): array
{
    $signatures = [];
    $contract = local_store_find($token);
    $tokens = [trim($token)];

    if ($contract !== null) {
        foreach (['token', 'sign_token', 'contract_token'] as $key) {
            $value = trim((string)($contract[$key] ?? ''));
            if ($value !== '' && !in_array($value, $tokens, true)) {
                $tokens[] = $value;
            }
        }
    }

    foreach (local_store_read()['signatures'] as $signature) {
        $signatureToken = trim((string)($signature['contract_token'] ?? ''));
        if ($signatureToken !== '' && in_array($signatureToken, $tokens, true)) {
            $signatures[] = $signature;
        }
    }

    usort($signatures, static function (array $a, array $b): int {
        return ((int)($a['owner_index'] ?? 0)) <=> ((int)($b['owner_index'] ?? 0));
    });

    return $signatures;
}

function local_store_save_owner_signature(string $token, array $signature): array
{
    $data = local_store_read();
    $signature['id'] = local_store_next_id($data, 'signatures');
    $data['signatures'][] = $signature;

    foreach ($data['contracts'] as $index => $contract) {
        if (!local_store_contract_matches($contract, $token)) {
            continue;
        }

        $contractToken = local_store_resolve_token($contract, $token);
        $completed = count(array_filter(
            $data['signatures'],
            static fn(array $item): bool => trim((string)($item['contract_token'] ?? '')) === $contractToken
                || trim((string)($item['contract_token'] ?? '')) === trim($token)
        ));
        $updates = [
            'signatures_completed' => $completed,
        ];

        if (!empty($signature['is_final'])) {
            $updates['status'] = 'signed';
            $updates['signed_at'] = $signature['signed_at'] ?? date('Y-m-d H:i:s');
            $updates['signer_name'] = $signature['signer_name'] ?? ($contract['signer_name'] ?? '');
            $updates['signed_pdf_path'] = $signature['signed_pdf_path'] ?? ($contract['signed_pdf_path'] ?? '');
        } else {
            $updates['status'] = 'signing';
        }

        $data['contracts'][$index] = array_merge($contract, $updates);
        break;
    }

    local_store_write($data);

    return $signature;
}

function local_store_save_signature(string $token, array $signature): array
{
    $signature['is_final'] = true;

    return local_store_save_owner_signature($token, $signature);
}

function local_store_log_link_access(string $token, array $access): ?array
{
    $data = local_store_read();
    $updated = null;

    foreach ($data['contracts'] as $index => $contract) {
        if (!local_store_contract_matches($contract, $token)) {
            continue;
        }

        $log = is_array($contract['link_access_log'] ?? null) ? $contract['link_access_log'] : [];
        $log[] = $access;

        $updates = [
            'link_access_log' => $log,
            'link_last_access' => $access,
        ];

        if (empty($contract['link_first_access'])) {
            $updates['link_first_access'] = $access;
        }

        $data['contracts'][$index] = array_merge($contract, $updates);
        $updated = $data['contracts'][$index];
        break;
    }

    if ($updated === null) {
        return null;
    }

    local_store_write($data);

    return $updated;
}
