<?php

declare(strict_types=1);

function app_config(): array
{
    static $config = null;
    if ($config === null) {
        $config = require __DIR__ . '/../config/config.php';
    }
    return $config;
}

function app_detect_web_base_from_script(): string
{
    $scriptName = str_replace('\\', '/', (string)($_SERVER['SCRIPT_NAME'] ?? '/index.php'));

    foreach (['/admin/', '/api/'] as $marker) {
        $pos = strpos($scriptName, $marker);
        if ($pos !== false) {
            return $pos === 0 ? '' : rtrim(substr($scriptName, 0, $pos), '/');
        }
    }

    if (str_ends_with($scriptName, '/index.php')) {
        $base = substr($scriptName, 0, -strlen('/index.php'));
        return $base === '' ? '' : rtrim($base, '/');
    }

    $dir = rtrim(str_replace('\\', '/', dirname($scriptName)), '/');
    if ($dir === '' || $dir === '/' || $dir === '.') {
        return '';
    }

    return $dir;
}

function app_project_root(): string
{
    if (defined('KARVI_ROOT')) {
        return KARVI_ROOT;
    }

    return dirname(__DIR__);
}

function app_detect_web_base_from_filesystem(): string
{
    $root = rtrim(str_replace('\\', '/', app_project_root()), '/');
    $docRoot = rtrim(str_replace('\\', '/', (string)($_SERVER['DOCUMENT_ROOT'] ?? '')), '/');

    if ($docRoot === '' || $root === '' || !str_starts_with($root, $docRoot)) {
        return '';
    }

    $base = substr($root, strlen($docRoot));
    $base = rtrim(str_replace('\\', '/', $base), '/');

    return ($base === '' || $base === '/') ? '' : $base;
}

function app_current_request_path(): string
{
    foreach (['REQUEST_URI', 'REDIRECT_URL', 'PATH_INFO'] as $key) {
        if (empty($_SERVER[$key])) {
            continue;
        }

        $path = parse_url((string)$_SERVER[$key], PHP_URL_PATH);
        if (is_string($path) && $path !== '') {
            return rtrim(str_replace('\\', '/', $path), '/') ?: '/';
        }
    }

    return '/';
}

function app_extract_app_route_from_path(string $path): ?string
{
    $path = rtrim(str_replace('\\', '/', $path), '/') ?: '/';

    foreach ([
        '/admin/login',
        '/admin/dashboard',
        '/admin/create',
        '/admin/contracts',
        '/admin/website',
        '/admin/pos',
        '/contracts/sign',
        '/contract/',
        '/api/',
    ] as $marker) {
        $pos = stripos($path, $marker);
        if ($pos !== false) {
            $route = substr($path, $pos);

            return rtrim($route, '/') ?: '/';
        }
    }

    if (preg_match('#(/install\.php)$#i', $path, $matches)) {
        return $matches[1];
    }

    if (preg_match('#(/health\.php)$#i', $path, $matches)) {
        return $matches[1];
    }

    if ($path === '/' || $path === '/index.php' || $path === '/default.php') {
        return '/';
    }

    return null;
}

function app_resolve_web_base_path(): string
{
    static $resolved = null;
    if ($resolved !== null) {
        return $resolved;
    }

    $fromScript = app_detect_web_base_from_script();
    $fromFilesystem = app_detect_web_base_from_filesystem();
    $requestPath = app_current_request_path();

    $configured = trim((string)(app_config()['base_path'] ?? ''));
    if ($configured !== '') {
        $configured = rtrim(str_replace('\\', '/', $configured), '/');
    }

    if (!empty($_SERVER['KARVI_WEB_BASE'])) {
        $forced = rtrim(str_replace('\\', '/', (string)$_SERVER['KARVI_WEB_BASE']), '/');
        $resolved = $forced === '/' ? '' : $forced;

        return $resolved;
    }

    if (PHP_SAPI === 'cli-server') {
        $scriptName = str_replace('\\', '/', (string)($_SERVER['SCRIPT_NAME'] ?? '/index.php'));
        if (!str_ends_with(strtolower($scriptName), '.php')) {
            $resolved = '';
            return $resolved;
        }
    }

    if ($fromFilesystem !== '' && str_contains($requestPath, 'Karvi_partner_contract')) {
        $needle = ltrim($fromFilesystem, '/');
        $pos = stripos($requestPath, $needle);
        if ($pos !== false) {
            $resolved = rtrim(substr($requestPath, 0, $pos + strlen($needle)), '/');
            if ($resolved !== '') {
                return $resolved;
            }
        }
    }

    $candidates = array_values(array_unique(array_filter([
        $fromFilesystem,
        $fromScript,
        $configured,
    ], static fn (string $value): bool => $value !== '')));

    $matching = [];
    foreach ($candidates as $candidate) {
        if ($requestPath === $candidate || str_starts_with($requestPath, $candidate . '/')) {
            $matching[] = $candidate;
        }
    }

    if ($matching !== []) {
        usort($matching, static fn (string $a, string $b): int => strlen($b) <=> strlen($a));
        $resolved = $matching[0];

        return $resolved;
    }

    if ($fromFilesystem !== '' && $fromScript !== '' && str_starts_with($fromFilesystem, $fromScript . '/')) {
        $resolved = $fromFilesystem;

        return $resolved;
    }

    if ($fromFilesystem !== '') {
        $resolved = $fromFilesystem;

        return $resolved;
    }

    if ($configured !== '') {
        $resolved = $configured;
        return $resolved;
    }

    $resolved = $fromScript;
    return $resolved;
}

function app_script_base_path(): string
{
    return app_resolve_web_base_path();
}

function app_maybe_redirect_canonical_request(): void
{
    if (!empty($_SERVER['KARVI_ROUTE']) || headers_sent()) {
        return;
    }

    $requestPath = app_current_request_path();
    $basePath = app_resolve_web_base_path();
    $route = app_extract_app_route_from_path($requestPath);

    if ($route === null || $basePath === '') {
        return;
    }

    if ($requestPath === $route || str_starts_with($requestPath, $basePath . $route)) {
        return;
    }

    if (!str_contains($requestPath, 'Karvi_partner_contract') && !str_contains($requestPath, '/link/')) {
        return;
    }

    $target = app_absolute_url($route);
    $current = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http')
        . '://'
        . ($_SERVER['HTTP_HOST'] ?? 'localhost')
        . $requestPath;

    if ($target !== '' && $target !== $current) {
        header('Location: ' . $target, true, 302);
        exit;
    }
}

function app_base_url(): string
{
    static $resolved = null;
    if ($resolved !== null) {
        return $resolved;
    }

    $configured = trim((string)(app_config()['base_url'] ?? ''));

    if ($configured !== '' && strtolower($configured) !== 'auto') {
        $resolved = rtrim($configured, '/');
        return $resolved;
    }

    $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
        || ((string)($_SERVER['SERVER_PORT'] ?? '') === '443')
        || (strtolower((string)($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '')) === 'https');

    $scheme = $https ? 'https' : 'http';
    $host = trim((string)($_SERVER['HTTP_HOST'] ?? ''));

    if ($host === '') {
        $resolved = '';
        return $resolved;
    }

    $basePath = app_script_base_path();
    $resolved = $scheme . '://' . $host . $basePath;

    return $resolved;
}

function app_base_path(): string
{
    static $resolved = null;
    if ($resolved !== null) {
        return $resolved;
    }

    $resolved = app_script_base_path();
    return $resolved;
}

function app_path(string $path = '/'): string
{
    $path = '/' . ltrim($path, '/');
    if ($path === '//') {
        $path = '/';
    }

    $base = app_base_path();
    if ($base === '') {
        return $path;
    }

    return $base . ($path === '/' ? '' : $path);
}

function app_absolute_url(string $path = '/'): string
{
    $path = '/' . ltrim($path, '/');
    if ($path === '//') {
        $path = '/';
    }

    return rtrim(app_base_url(), '/') . ($path === '/' ? '' : $path);
}

function app_asset(string $path): string
{
    return app_path('/assets/' . ltrim($path, '/'));
}

function app_public_script(string $path): string
{
    if ($path === '') {
        return '';
    }
    if (str_starts_with($path, '/assets/')) {
        return app_asset(substr($path, 8));
    }
    if (str_starts_with($path, '/')) {
        return app_path($path);
    }
    return $path;
}

function is_valid_contract_token(string $token): bool
{
    return (bool)preg_match('/^[a-z0-9]{32}$/', $token);
}

function contract_token_path_regex(): string
{
    return '#^/contract/[a-z0-9]{32}$#';
}

function current_app_uri(): string
{
    $uris = [];

    if (!empty($_SERVER['REQUEST_URI'])) {
        $uris[] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) ?: '/';
    }
    if (!empty($_SERVER['REDIRECT_URL'])) {
        $uris[] = parse_url((string)$_SERVER['REDIRECT_URL'], PHP_URL_PATH) ?: '';
    }
    if (!empty($_SERVER['PATH_INFO'])) {
        $uris[] = (string)$_SERVER['PATH_INFO'];
    }

    $base = app_base_path();
    $query = parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_QUERY);
    $querySuffix = (is_string($query) && $query !== '') ? '?' . $query : '';

    foreach ($uris as $uri) {
        if ($uri === '' || $uri === '/') {
            continue;
        }

        if ($base !== '' && str_starts_with($uri, $base)) {
            $uri = substr($uri, strlen($base)) ?: '/';
        }

        $normalized = rtrim($uri, '/') ?: '/';
        if (preg_match(contract_token_path_regex(), $normalized)) {
            return $normalized . $querySuffix;
        }
    }

    foreach ($uris as $uri) {
        if ($uri === '' || $uri === '/') {
            continue;
        }

        if ($base !== '' && str_starts_with($uri, $base)) {
            $uri = substr($uri, strlen($base)) ?: '/';
        }

        return (rtrim($uri, '/') ?: '/') . $querySuffix;
    }

    return '/';
}

function locale_return_from_referer(): string
{
    $referer = $_SERVER['HTTP_REFERER'] ?? '';
    if ($referer === '') {
        return '';
    }

    $path = parse_url($referer, PHP_URL_PATH) ?: '';
    $base = app_base_path();

    if ($base !== '' && str_starts_with($path, $base)) {
        $path = substr($path, strlen($base)) ?: '/';
    }

    $path = rtrim($path, '/') ?: '/';
    if (!preg_match(contract_token_path_regex(), $path)) {
        return '';
    }

    $query = parse_url($referer, PHP_URL_QUERY);
    if (is_string($query) && $query !== '') {
        $path .= '?' . $query;
    }

    return $path;
}

function normalize_locale_return(string $return): string
{
    $return = trim($return);

    if (
        $return === ''
        || $return === '/'
        || !str_starts_with($return, '/')
        || str_starts_with($return, '//')
        || str_contains($return, '://')
    ) {
        $return = '';
    }

    if ($return === '') {
        $return = locale_return_from_referer();
    }

    if ($return === '') {
        return '/';
    }

    $pathOnly = parse_url($return, PHP_URL_PATH) ?: '/';
    $pathOnly = rtrim($pathOnly, '/') ?: '/';

    if (preg_match('#^/lang/(en|de)$#', $pathOnly)) {
        $return = locale_return_from_referer();
        if ($return === '') {
            return '/';
        }
        $pathOnly = parse_url($return, PHP_URL_PATH) ?: '/';
        $pathOnly = rtrim($pathOnly, '/') ?: '/';
    }

    $query = parse_url($return, PHP_URL_QUERY);
    $normalized = $pathOnly;

    if (is_string($query) && $query !== '') {
        $normalized .= '?' . $query;
    }

    return $normalized;
}

function generate_token(): string
{
    return bin2hex(random_bytes(16));
}

function client_ip(): string
{
    $headers = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'];
    foreach ($headers as $header) {
        if (empty($_SERVER[$header])) {
            continue;
        }

        $value = (string)$_SERVER[$header];
        if ($header === 'HTTP_X_FORWARDED_FOR') {
            $parts = explode(',', $value);
            $value = trim($parts[0]);
        }

        if (filter_var($value, FILTER_VALIDATE_IP)) {
            return $value;
        }

        if ($value !== '') {
            return $value;
        }
    }

    return 'unknown';
}

function json_response(array $data, int $status = 200): never
{
    http_response_code($status);
    header('Content-Type: application/json');
    echo json_encode($data);
    exit;
}

function redirect(string $path): never
{
    if (!preg_match('#^https?://#i', $path)) {
        $path = app_path($path);
    }

    if (session_status() === PHP_SESSION_ACTIVE) {
        session_write_close();
    }

    header('Location: ' . $path);
    exit;
}

function e(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}

function contract_customer_path(string $signToken = ''): string
{
    $signToken = trim($signToken);
    if ($signToken === '') {
        return app_path('/contract');
    }

    return app_path('/contract/' . $signToken);
}

function contract_public_sign_path(string $signToken = ''): string
{
    $signToken = trim($signToken);
    if ($signToken === '') {
        return app_path('/contracts/sign');
    }

    return app_path('/contracts/sign/' . rawurlencode($signToken));
}

function contract_public_sign_url(string $signToken): string
{
    $signToken = trim($signToken);
    if ($signToken === '') {
        return '';
    }

    return app_absolute_url('/contracts/sign/' . rawurlencode($signToken));
}

function contract_public_sign_url_from_record(array $contract): string
{
    $signToken = trim((string)($contract['sign_token'] ?? ''));
    if ($signToken !== '') {
        return contract_public_sign_url($signToken);
    }

    return contract_customer_url($contract);
}

function contract_customer_path_from_record(array $contract): string
{
    $signToken = trim((string)($contract['sign_token'] ?? ''));
    if ($signToken !== '') {
        return contract_public_sign_path($signToken);
    }

    return contract_customer_path((string)($contract['token'] ?? ''));
}

function contract_url(string $token): string
{
    $token = trim($token);
    if ($token === '') {
        return app_absolute_url('/contract');
    }

    return app_absolute_url('/contract/' . rawurlencode($token));
}

function contract_customer_url(array $contract): string
{
    $token = trim((string)($contract['sign_token'] ?? $contract['token'] ?? ''));
    if ($token === '') {
        return '';
    }

    return contract_url($token);
}

function contract_signed_pdf_filename(array $contract): string
{
    $resolved = contract_resolve_signed_pdf_disk_path($contract);
    if ($resolved !== null) {
        return basename($resolved);
    }

    $token = contract_admin_token($contract);

    return basename((string)($contract['signed_pdf_path'] ?? ($token . '_signed.pdf')));
}

function contract_signed_pdf_path(array $contract): string
{
    $resolved = contract_resolve_signed_pdf_disk_path($contract);
    if ($resolved !== null) {
        return $resolved;
    }

    return app_config()['signed_dir'] . '/' . contract_signed_pdf_filename($contract);
}

function contract_signed_pdf_available(array $contract): bool
{
    if (($contract['status'] ?? '') !== 'signed') {
        return false;
    }

    return contract_resolve_signed_pdf_disk_path($contract) !== null;
}

function contract_public_pdf_url(array $contract, bool $download = false): string
{
    $token = contract_admin_token($contract);
    $url = app_absolute_url('/contract/' . rawurlencode($token) . '/pdf');

    return $download ? $url . '?download=1' : $url;
}

function contract_signed_pdf_download_url(array $contract): string
{
    return contract_public_pdf_url($contract, true);
}

function contract_pdf_filename(array $contract): string
{
    $token = trim(contract_admin_token($contract));
    $fromPath = trim((string)($contract['pdf_path'] ?? ''));
    if ($fromPath !== '') {
        $basename = basename($fromPath);

        return $basename !== '' && $basename !== '.' ? $basename : ($token !== '' ? $token . '.pdf' : generate_token() . '.pdf');
    }

    return $token !== '' ? $token . '.pdf' : generate_token() . '.pdf';
}

function contract_pdf_path(array $contract): string
{
    $resolved = contract_resolve_contract_pdf_disk_path($contract);
    if ($resolved !== null) {
        return $resolved;
    }

    return app_config()['contracts_dir'] . '/' . contract_pdf_filename($contract);
}

function contract_pdf_available(array $contract): bool
{
    return contract_resolve_contract_pdf_disk_path($contract) !== null;
}

function contract_delete_unsigned_pdf(array $contract, ?string $contractPdfPath = null): bool
{
    if (($contract['status'] ?? '') !== 'signed' && $contractPdfPath === null) {
        return false;
    }

    $path = $contractPdfPath ?? contract_pdf_path($contract);
    $path = trim($path);

    if ($path === '' || !is_file($path)) {
        return false;
    }

    $signedPath = contract_signed_pdf_path($contract);
    if (is_file($signedPath) && realpath($path) === realpath($signedPath)) {
        return false;
    }

    if (!@unlink($path)) {
        return false;
    }

    $token = trim((string)($contract['token'] ?? $contract['sign_token'] ?? ''));
    if ($token !== '') {
        require_once __DIR__ . '/local_store.php';
        local_store_update($token, ['pdf_path' => '']);
    }

    return true;
}

function contract_pdf_download_url(array $contract): string
{
    return contract_public_pdf_url($contract, true);
}

function contract_persist_local_contracts(): bool
{
    return (bool)(app_config()['persist_local_contracts'] ?? true);
}

function contract_download_available(array $contract): bool
{
    if (contract_signed_pdf_available($contract)) {
        return true;
    }

    require_once __DIR__ . '/invoice_contract.php';
    if (invoice_contract_is($contract)) {
        return false;
    }

    return contract_pdf_available($contract) || (($contract['status'] ?? '') === 'signed');
}

function uploads_relative_path(string $subdir, string $filename): string
{
    $filename = basename($filename);
    $subdir = trim(str_replace('\\', '/', $subdir), '/');

    return 'uploads/' . $subdir . '/' . $filename;
}

function contract_link_location(array $contract): ?array
{
    $access = $contract['link_last_access'] ?? $contract['link_first_access'] ?? null;
    if (!is_array($access)) {
        return null;
    }

    $hasCoordinates = isset($access['latitude'], $access['longitude'])
        && $access['latitude'] !== null
        && $access['longitude'] !== null;

    if ($hasCoordinates || trim((string)($access['full_address'] ?? '')) !== '') {
        return $access;
    }

    return null;
}

function location_audit_fields(?array $access): array
{
    if ($access === null) {
        return [];
    }

    $fields = [
        'latitude' => $access['latitude'] ?? null,
        'longitude' => $access['longitude'] ?? null,
        'full_address' => trim((string)($access['full_address'] ?? '')),
        'accuracy_meters' => $access['accuracy_meters'] ?? null,
        'geo_status' => trim((string)($access['geo_status'] ?? '')),
        'location_captured_at' => trim((string)($access['accessed_at'] ?? '')),
        'location_timezone' => trim((string)($access['timezone'] ?? '')),
        'location_screen_resolution' => trim((string)($access['screen_resolution'] ?? '')),
        'location_user_agent' => trim((string)($access['user_agent'] ?? '')),
    ];

    foreach ($fields as $key => $value) {
        if ($value === null || $value === '') {
            unset($fields[$key]);
        }
    }

    return $fields;
}

function signature_audit_records(array $signatures): array
{
    $records = [];

    foreach ($signatures as $signature) {
        if (!is_array($signature)) {
            continue;
        }

        $record = [
            'signer_name' => (string)($signature['signer_name'] ?? ''),
            'signed_at' => (string)($signature['signed_at'] ?? ''),
            'ip_address' => (string)($signature['ip_address'] ?? ''),
            'user_agent' => (string)($signature['user_agent'] ?? ''),
            'screen_resolution' => (string)($signature['screen_resolution'] ?? ''),
            'timezone' => (string)($signature['timezone'] ?? ''),
            'signature_method' => (string)($signature['signature_method'] ?? ''),
        ];

        foreach (location_audit_fields($signature) as $key => $value) {
            $record[$key] = $value;
        }

        $records[] = $record;
    }

    return $records;
}

function contract_reference(array $contract): string
{
    return 'KP-' . str_pad((string)($contract['id'] ?? 0), 5, '0', STR_PAD_LEFT);
}

function contract_all_tokens(array $contract): array
{
    $tokens = [];

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

    $signedPdf = basename((string)($contract['signed_pdf_path'] ?? ''));
    if ($signedPdf !== '' && str_ends_with($signedPdf, '_signed.pdf')) {
        $fromPdf = substr($signedPdf, 0, -strlen('_signed.pdf'));
        if ($fromPdf !== '' && !in_array($fromPdf, $tokens, true)) {
            $tokens[] = $fromPdf;
        }
    }

    return $tokens;
}

function contract_admin_token(array $contract): string
{
    require_once __DIR__ . '/local_store.php';

    return local_store_resolve_token($contract);
}

function contract_resolve_signed_pdf_disk_path(array $contract): ?string
{
    $config = app_config();
    $candidates = [];

    $fromField = basename((string)($contract['signed_pdf_path'] ?? ''));
    if ($fromField !== '') {
        $candidates[] = $config['signed_dir'] . '/' . $fromField;
    }

    foreach (contract_all_tokens($contract) as $token) {
        $candidates[] = $config['signed_dir'] . '/' . $token . '_signed.pdf';
        foreach (glob($config['signed_dir'] . '/*' . $token . '*') ?: [] as $file) {
            $candidates[] = $file;
        }
    }

    foreach (array_unique($candidates) as $path) {
        if (is_file($path)) {
            return $path;
        }
    }

    return null;
}

function contract_resolve_contract_pdf_disk_path(array $contract): ?string
{
    $config = app_config();
    $candidates = [];

    $fromField = basename((string)($contract['pdf_path'] ?? ''));
    if ($fromField !== '') {
        $candidates[] = $config['contracts_dir'] . '/' . $fromField;
    }

    foreach (contract_all_tokens($contract) as $token) {
        $candidates[] = $config['contracts_dir'] . '/' . $token . '.pdf';
        foreach (glob($config['contracts_dir'] . '/*' . $token . '*') ?: [] as $file) {
            $candidates[] = $file;
        }
    }

    foreach (array_unique($candidates) as $path) {
        if (is_file($path)) {
            return $path;
        }
    }

    return null;
}

function signature_safe_filename_part(string $value, string $fallback = 'signature'): string
{
    $value = trim($value);
    $value = preg_replace('/[^\p{L}\p{N}\s._-]/u', '', $value) ?? '';
    $value = preg_replace('/\s+/', '_', $value) ?? '';
    $value = trim($value, '._-');

    if ($value === '') {
        return $fallback;
    }

    return mb_substr($value, 0, 60);
}

function signature_export_filename(array $contract, array $signature, int $index, int $total): string
{
    $ref = contract_reference($contract);
    $signer = signature_safe_filename_part((string)($signature['signer_name'] ?? ''), 'signer');

    if ($total > 1) {
        return $ref . '_' . $signer . '_' . ($index + 1) . '.png';
    }

    return $ref . '_' . $signer . '.png';
}

function contract_signature_exports(array $contract): array
{
    require_once __DIR__ . '/api_service.php';

    $signatures = api_get_signatures_for_contract_record($contract);
    $config = app_config();
    $exports = [];
    $seenPaths = [];
    $total = count($signatures);

    foreach ($signatures as $index => $signature) {
        $image = $config['signatures_dir'] . '/' . basename((string)($signature['signature_image'] ?? ''));
        if (!is_file($image) || isset($seenPaths[$image])) {
            continue;
        }

        $seenPaths[$image] = true;
        $exports[] = [
            'path' => $image,
            'filename' => signature_export_filename($contract, $signature, (int)$index, $total),
        ];
    }

    if ($exports !== []) {
        return $exports;
    }

    foreach (contract_all_tokens($contract) as $token) {
        foreach (glob($config['signatures_dir'] . '/' . $token . '_*') ?: [] as $file) {
            if (!is_file($file) || isset($seenPaths[$file])) {
                continue;
            }

            $seenPaths[$file] = true;
            $exports[] = [
                'path' => $file,
                'filename' => signature_export_filename(
                    $contract,
                    ['signature_image' => basename($file), 'signer_name' => ''],
                    count($exports),
                    count($exports) + 1
                ),
            ];
        }
    }

    return $exports;
}

function contract_signatures_available(array $contract): bool
{
    return contract_signature_exports($contract) !== [];
}

function admin_contract_signatures_download_url(array $contract): string
{
    $token = contract_admin_token($contract);
    $url = app_absolute_url('/admin/download-signatures?token=' . rawurlencode($token));
    $id = (int)($contract['id'] ?? 0);

    if ($id > 0) {
        $url .= '&id=' . $id;
    }

    return $url;
}

function contract_signed_pdf_needs_regeneration(array $contract): bool
{
    if (!contract_signed_pdf_available($contract) || !contract_pdf_available($contract)) {
        return !contract_signed_pdf_available($contract);
    }

    $signedSize = (int)filesize(contract_signed_pdf_path($contract));
    $contractSize = (int)filesize(contract_pdf_path($contract));

    if ($contractSize < 1) {
        return false;
    }

    return $signedSize <= (int)round($contractSize * 1.08);
}

function contract_ensure_signed_pdf(array $contract): bool
{
    if (contract_signed_pdf_available($contract) && !contract_signed_pdf_needs_regeneration($contract)) {
        return true;
    }

    if (($contract['status'] ?? '') !== 'signed') {
        return false;
    }

    require_once __DIR__ . '/api_service.php';
    require_once __DIR__ . '/pdf_generator.php';
    require_once __DIR__ . '/invoice_pdf_generator.php';

    $token = contract_admin_token($contract);
    if ($token === '') {
        return false;
    }

    $signatures = api_get_signatures_for_contract_record($contract);
    if ($signatures === []) {
        return false;
    }

    $config = app_config();
    $signaturePaths = [];

    foreach ($signatures as $signature) {
        $image = $config['signatures_dir'] . '/' . basename((string)($signature['signature_image'] ?? ''));
        if (is_file($image)) {
            $signaturePaths[] = $image;
        }
    }

    if ($signaturePaths === []) {
        return false;
    }

    foreach ($signaturePaths as $path) {
        prepare_signature_image_for_pdf($path);
    }

    try {
        $contractPdfFilename = generate_contract_pdf_for_record($contract);
        $contractPdfPath = $config['contracts_dir'] . '/' . $contractPdfFilename;
        $signedPdfFilename = contract_signed_pdf_filename($contract);
        $signedPdfPath = $config['signed_dir'] . '/' . $signedPdfFilename;

        append_signatures_to_pdf(
            $contractPdfPath,
            $signaturePaths,
            $signedPdfPath,
            signature_audit_records($signatures),
            invoice_contract_is($contract) ? 'invoice' : 'partner'
        );

        if (!is_file($signedPdfPath)) {
            return false;
        }

        contract_delete_unsigned_pdf(
            array_merge($contract, ['status' => 'signed', 'signed_pdf_path' => $signedPdfFilename]),
            $contractPdfPath
        );

        api_update_contract($token, [
            'pdf_path' => '',
            'signed_pdf_path' => $signedPdfFilename,
        ]);

        return true;
    } catch (Throwable) {
        return false;
    }
}

function contract_download_url(array $contract): string
{
    if (contract_signed_pdf_available($contract)) {
        return contract_signed_pdf_download_url($contract);
    }

    return contract_pdf_download_url($contract);
}

function admin_contract_download_url(array $contract): string
{
    $token = contract_admin_token($contract);
    $url = app_absolute_url('/admin/download?token=' . rawurlencode($token));
    $id = (int)($contract['id'] ?? 0);

    if ($id > 0) {
        $url .= '&id=' . $id;
    }

    return $url;
}

function format_date(?string $date): string
{
    if (!$date) {
        return '-';
    }

    $timestamp = strtotime($date);
    if ($timestamp === false) {
        return '-';
    }

    if (current_locale() === 'de') {
        $months = ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'];
        $month = $months[(int)date('n', $timestamp) - 1] ?? date('M', $timestamp);
        return date('d', $timestamp) . '. ' . $month . ' ' . date('Y', $timestamp);
    }

    return date('d M Y', $timestamp);
}

function signature_export_transparent_file(string $sourcePath): ?string
{
    if (!is_file($sourcePath) || !function_exists('imagecreatefromstring')) {
        return null;
    }

    $blob = file_get_contents($sourcePath);
    if ($blob === false) {
        return null;
    }

    $source = @imagecreatefromstring($blob);
    if ($source === false) {
        return null;
    }

    $width = imagesx($source);
    $height = imagesy($source);
    if ($width < 1 || $height < 1) {
        imagedestroy($source);
        return null;
    }

    $dest = imagecreatetruecolor($width, $height);
    if ($dest === false) {
        imagedestroy($source);
        return null;
    }

    imagesavealpha($dest, true);
    $transparent = imagecolorallocatealpha($dest, 0, 0, 0, 127);
    imagefill($dest, 0, 0, $transparent);

    for ($y = 0; $y < $height; $y++) {
        for ($x = 0; $x < $width; $x++) {
            $rgba = imagecolorat($source, $x, $y);
            $alpha = ($rgba & 0x7F000000) >> 24;
            $red = ($rgba >> 16) & 0xFF;
            $green = ($rgba >> 8) & 0xFF;
            $blue = $rgba & 0xFF;

            if ($red > 245 && $green > 245 && $blue > 245) {
                continue;
            }

            if ($alpha >= 120) {
                continue;
            }

            $color = imagecolorallocatealpha($dest, $red, $green, $blue, min(127, $alpha));
            imagesetpixel($dest, $x, $y, $color);
        }
    }

    imagedestroy($source);

    $tempBase = tempnam(sys_get_temp_dir(), 'karvi_sig_export_');
    if ($tempBase === false) {
        imagedestroy($dest);
        return null;
    }

    $tempPath = $tempBase . '.png';
    @unlink($tempBase);

    if (!imagepng($dest, $tempPath)) {
        imagedestroy($dest);
        @unlink($tempPath);
        return null;
    }

    imagedestroy($dest);

    return $tempPath;
}

function contract_signature_download_exports(array $contract): array
{
    $exports = contract_signature_exports($contract);
    $prepared = [];

    foreach ($exports as $export) {
        $transparentPath = signature_export_transparent_file($export['path']);
        $prepared[] = [
            'path' => $transparentPath ?? $export['path'],
            'filename' => $export['filename'],
            'temporary' => $transparentPath !== null,
        ];
    }

    return $prepared;
}

function contract_signature_download_as_zip(array $contract, array $exports): bool
{
    if (count($exports) > 1) {
        return true;
    }

    require_once __DIR__ . '/registration_wizard.php';

    return registration_owner_count($contract) > 1;
}

function flatten_signature_image_on_white(string $path): bool
{
    if (!is_file($path) || !function_exists('imagecreatefromstring')) {
        return false;
    }

    $blob = file_get_contents($path);
    if ($blob === false) {
        return false;
    }

    $source = @imagecreatefromstring($blob);
    if ($source === false) {
        return false;
    }

    $width = imagesx($source);
    $height = imagesy($source);
    if ($width < 1 || $height < 1) {
        imagedestroy($source);
        return false;
    }

    $flat = imagecreatetruecolor($width, $height);
    if ($flat === false) {
        imagedestroy($source);
        return false;
    }

    $white = imagecolorallocate($flat, 255, 255, 255);
    imagefill($flat, 0, 0, $white);
    imagealphablending($flat, true);
    imagesavealpha($flat, false);
    imagecopy($flat, $source, 0, 0, 0, 0, $width, $height);
    imagedestroy($source);

    $saved = imagepng($flat, $path);
    imagedestroy($flat);

    return $saved;
}

function prepare_signature_image_for_pdf(string $path): void
{
    crop_signature_image_file($path);
    flatten_signature_image_on_white($path);
}

function crop_signature_image_file(string $path, int $padding = 10): bool
{
    if (!is_file($path) || !function_exists('imagecreatefromstring')) {
        return false;
    }

    $blob = file_get_contents($path);
    if ($blob === false) {
        return false;
    }

    $source = @imagecreatefromstring($blob);
    if ($source === false) {
        return false;
    }

    imagesavealpha($source, true);

    $width = imagesx($source);
    $height = imagesy($source);
    $minX = $width;
    $minY = $height;
    $maxX = 0;
    $maxY = 0;
    $found = false;

    for ($y = 0; $y < $height; $y++) {
        for ($x = 0; $x < $width; $x++) {
            $rgba = imagecolorat($source, $x, $y);
            $alpha = ($rgba & 0x7F000000) >> 24;
            $red = ($rgba >> 16) & 0xFF;
            $green = ($rgba >> 8) & 0xFF;
            $blue = $rgba & 0xFF;

            if ($alpha >= 120) {
                continue;
            }

            if ($red > 250 && $green > 250 && $blue > 250) {
                continue;
            }

            $found = true;
            $minX = min($minX, $x);
            $minY = min($minY, $y);
            $maxX = max($maxX, $x);
            $maxY = max($maxY, $y);
        }
    }

    if (!$found) {
        imagedestroy($source);
        return false;
    }

    $minX = max(0, $minX - $padding);
    $minY = max(0, $minY - $padding);
    $maxX = min($width - 1, $maxX + $padding);
    $maxY = min($height - 1, $maxY + $padding);
    $cropWidth = $maxX - $minX + 1;
    $cropHeight = $maxY - $minY + 1;

    if ($cropWidth < 1 || $cropHeight < 1) {
        imagedestroy($source);
        return false;
    }

    $cropped = imagecreatetruecolor($cropWidth, $cropHeight);
    if ($cropped === false) {
        imagedestroy($source);
        return false;
    }

    imagesavealpha($cropped, true);
    $transparent = imagecolorallocatealpha($cropped, 0, 0, 0, 127);
    imagefill($cropped, 0, 0, $transparent);
    imagecopy($cropped, $source, 0, 0, $minX, $minY, $cropWidth, $cropHeight);
    imagedestroy($source);

    $saved = imagepng($cropped, $path);
    imagedestroy($cropped);

    return $saved;
}

function reverse_geocode_coordinates(float $latitude, float $longitude): string
{
    $url = sprintf(
        'https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=%F&lon=%F',
        $latitude,
        $longitude
    );

    $context = stream_context_create([
        'http' => [
            'method' => 'GET',
            'header' => "User-Agent: KarviPartnerContract/1.0 (contact@karvi.de)\r\nAccept: application/json\r\n",
            'timeout' => 6,
        ],
    ]);

    $response = @file_get_contents($url, false, $context);
    if ($response === false) {
        return '';
    }

    $data = json_decode($response, true);
    if (!is_array($data)) {
        return '';
    }

    return trim((string)($data['display_name'] ?? ''));
}
