<?php

declare(strict_types=1);

require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/local_store.php';

function invoice_contract_modes(): array
{
    return ['website', 'pos'];
}

function invoice_pos_contract_types(): array
{
    return [
        'karvi_pos' => [
            'key' => 'karvi_pos',
            'label_key' => 'invoice_pos_type_karvi',
            'contract_line' => 'Karvi POS',
            'default_line_item' => [
                'description' => 'Karvi Pro Kassensystem',
                'quantity' => 1,
                'unit_price_net' => 1904.0,
            ],
        ],
        'tse_lieferando' => [
            'key' => 'tse_lieferando',
            'label_key' => 'invoice_pos_type_tse',
            'contract_line' => 'TSE Lieferando',
            'default_line_item' => [
                'description' => 'TSE-System & ALL-IN-ONE Bestellintegration – Monatliche Gebühren fallen an.',
                'quantity' => 1,
                'unit_price_net' => 12.0,
            ],
        ],
    ];
}

function invoice_pos_contract_type_config(string $type): array
{
    $types = invoice_pos_contract_types();

    return $types[$type] ?? $types['karvi_pos'];
}

function invoice_mode_has_optional_printer(string $mode): bool
{
    $config = invoice_contract_mode_config($mode);

    return !empty($config['optional_item']) && is_array($config['optional_item']);
}

function invoice_pos_service_catalog(): array
{
    return [
        [
            'id' => 'software_license',
            'title' => 'SOFTWARE LICENSE',
            'price' => 1600.0,
            'suffix' => 'Net',
        ],
        [
            'id' => 'annual_maintenance',
            'title' => 'ANNUAL MAINTENANCE & SUPPORT',
            'price' => 175.0,
            'suffix' => 'Net / year',
        ],
        [
            'id' => 'caller_id',
            'title' => 'Caller ID (one-time)',
            'price' => 150.0,
            'suffix' => 'Net',
        ],
        [
            'id' => 'table_stickers',
            'title' => 'With printed table stickers',
            'price' => 250.0,
            'suffix' => 'Net',
        ],
        [
            'id' => 'online_ordering',
            'title' => 'ONLINE ORDERING SOLUTION',
            'price' => 1850.0,
            'suffix' => 'Net',
        ],
    ];
}

function invoice_pos_display_catalog(): array
{
    return [
        [
            'id' => 'screen_15_touch',
            'title' => '15" Touch screen',
            'price' => 395.0,
            'suffix' => 'Net',
        ],
        [
            'id' => 'screen_17_touch',
            'title' => '17" Touch screen',
            'price' => 450.0,
            'suffix' => 'Net',
        ],
        [
            'id' => 'screen_21_touch',
            'title' => '21" Touch screen',
            'price' => 490.0,
            'suffix' => 'Net',
        ],
        [
            'id' => 'screen_27_touch',
            'title' => '27" Touch screen',
            'price' => 530.0,
            'suffix' => 'Net',
        ],
        [
            'id' => 'screen_20_no_touch',
            'title' => '20" Screen without touch',
            'price' => 420.0,
            'suffix' => 'Net',
        ],
    ];
}

function invoice_contract_mode_config(string $mode): array
{
    return match ($mode) {
        'website' => [
            'mode' => 'website',
            'contract_line' => 'Karvi Website',
            'list_path' => '/admin/website',
            'create_path' => '/admin/website/create',
            'link_path' => '/admin/website/link',
            'list_page' => 'website',
            'create_page' => 'website_create',
            'title_key' => 'title_karvi_website',
            'list_title_key' => 'invoice_website_list_title',
            'list_sub_key' => 'invoice_website_list_sub',
            'form_title_key' => 'title_karvi_website',
            'nav_key' => 'nav_karvi_website',
            'icon' => 'language',
            'ref_prefix' => 'KW',
            'default_line_item' => [
                'description' => 'Entwicklung eines maßgeschneiderten Online-Bestellsystems für die Website mit Android- und iOS-Apps',
                'quantity' => 1,
                'unit_price_net' => 1950.0,
            ],
            'optional_item' => [
                'description' => 'Thermodrucker (80x80)',
                'unit_price_net' => 95.0,
            ],
        ],
        'pos' => [
            'mode' => 'pos',
            'contract_line' => 'Karvi POS',
            'list_path' => '/admin/pos',
            'create_path' => '/admin/pos/create',
            'link_path' => '/admin/pos/link',
            'list_page' => 'pos',
            'create_page' => 'pos_create',
            'title_key' => 'title_karvi_pos',
            'list_title_key' => 'invoice_pos_list_title',
            'list_sub_key' => 'invoice_pos_list_sub',
            'form_title_key' => 'title_karvi_pos',
            'nav_key' => 'nav_karvi_pos',
            'icon' => 'point_of_sale',
            'ref_prefix' => 'KP',
            'default_line_item' => [
                'description' => '',
                'quantity' => 1,
                'unit_price_net' => 0.0,
            ],
            'optional_item' => null,
        ],
        default => throw new InvalidArgumentException('Unknown invoice contract mode.'),
    };
}

function invoice_contract_is(array $contract): bool
{
    $line = trim((string)($contract['contract_line'] ?? ''));

    return in_array($line, invoice_all_invoice_contract_lines(), true);
}

function invoice_all_invoice_contract_lines(): array
{
    return ['Karvi Website', 'Karvi POS', 'TSE Lieferando'];
}

function invoice_contract_lines_for_mode(string $mode): array
{
    if ($mode === 'pos') {
        return array_map(
            static fn(array $type): string => (string)$type['contract_line'],
            invoice_pos_contract_types()
        );
    }

    return [invoice_contract_mode_config($mode)['contract_line']];
}

function invoice_contract_is_partner_record(array $contract): bool
{
    return !invoice_contract_is($contract);
}

function invoice_contract_line_for_mode(string $mode): string
{
    return invoice_contract_mode_config($mode)['contract_line'];
}

function invoice_parse_money(mixed $value): float
{
    if (is_int($value) || is_float($value)) {
        return round((float)$value, 2);
    }

    $normalized = trim(str_replace(["\xc2\xa0", '€', 'EUR', ' '], '', (string)$value));
    if ($normalized === '') {
        return 0.0;
    }

    if (str_contains($normalized, ',')) {
        $normalized = str_replace('.', '', $normalized);
        $normalized = str_replace(',', '.', $normalized);
    }

    return round((float)$normalized, 2);
}

function invoice_format_money(float $amount): string
{
    return number_format($amount, 2, ',', '.') . ' €';
}

function invoice_pos_default_line_items(string $posType): array
{
    $posLineItem = invoice_pos_contract_type_config('karvi_pos')['default_line_item'];
    $tseLineItem = invoice_pos_contract_type_config('tse_lieferando')['default_line_item'];
    $tseRow = [
        'description' => (string)$tseLineItem['description'],
        'quantity' => (string)(int)($tseLineItem['quantity'] ?? 1),
        'unit_price_net' => invoice_format_money((float)($tseLineItem['unit_price_net'] ?? 12.0)),
    ];

    if ($posType === 'tse_lieferando') {
        return [$tseRow];
    }

    return [[
        'description' => (string)$posLineItem['description'],
        'quantity' => (string)(int)($posLineItem['quantity'] ?? 1),
        'unit_price_net' => invoice_format_money((float)($posLineItem['unit_price_net'] ?? 0)),
    ]];
}

function invoice_default_form_values(string $mode, string $posType = 'karvi_pos'): array
{
    $config = invoice_contract_mode_config($mode);
    $lineItem = $config['default_line_item'];

    if ($mode === 'pos') {
        $lineItems = invoice_pos_default_line_items($posType);
    } else {
        $lineItems = [[
            'description' => (string)$lineItem['description'],
            'quantity' => (string)(int)($lineItem['quantity'] ?? 1),
            'unit_price_net' => invoice_format_money((float)($lineItem['unit_price_net'] ?? 0)),
        ]];
    }

    $optionalItem = $config['optional_item'] ?? null;

    return [
        'contract_date' => date('Y-m-d'),
        'installment_months' => '12',
        'customer_details' => '',
        'discount' => '0',
        'optional_printer' => '0',
        'pos_contract_type' => $mode === 'pos' ? $posType : '',
        'line_items' => $lineItems,
        'optional_item' => is_array($optionalItem) ? $optionalItem : ['description' => '', 'unit_price_net' => 0],
    ];
}

function invoice_line_items_from_post(array $post): array
{
    $items = [];
    $descriptions = $post['line_description'] ?? [];
    $quantities = $post['line_quantity'] ?? [];
    $unitPrices = $post['line_unit_price'] ?? [];

    if (!is_array($descriptions)) {
        $descriptions = trim((string)$descriptions) === '' ? [] : [(string)$descriptions];
    }

    if (!is_array($quantities)) {
        $quantities = [$quantities];
    }

    if (!is_array($unitPrices)) {
        $unitPrices = [$unitPrices];
    }

    foreach ($descriptions as $index => $description) {
        $description = trim((string)$description);
        if ($description === '') {
            continue;
        }

        $quantity = max(1, (int)($quantities[$index] ?? 1));
        $unitPrice = invoice_parse_money($unitPrices[$index] ?? 0);

        $items[] = [
            'description' => $description,
            'quantity' => $quantity,
            'unit_price_net' => $unitPrice,
            'total_net' => round($quantity * $unitPrice, 2),
        ];
    }

    return $items;
}

function invoice_calculate_totals(array $lineItems, float $discount, bool $includeOptionalPrinter, array $optionalItem): array
{
    $subtotal = 0.0;

    foreach ($lineItems as $item) {
        $subtotal += (float)($item['total_net'] ?? 0);
    }

    if ($includeOptionalPrinter) {
        $subtotal += (float)($optionalItem['unit_price_net'] ?? 0);
    }

    $discount = max(0.0, $discount);
    $net = max(0.0, round($subtotal - $discount, 2));
    $vat = round($net * 0.19, 2);
    $total = round($net + $vat, 2);

    return [
        'subtotal' => round($subtotal, 2),
        'discount' => $discount,
        'net' => $net,
        'vat_rate' => 19,
        'vat' => $vat,
        'total' => $total,
    ];
}

function invoice_contract_totals(array $contract): array
{
    $mode = (string)($contract['invoice_mode'] ?? 'website');
    $optionalItem = is_array($contract['optional_item'] ?? null)
        ? $contract['optional_item']
        : (invoice_contract_mode_config($mode)['optional_item'] ?? ['unit_price_net' => 0]);

    if (is_array($contract['totals'] ?? null)) {
        return $contract['totals'];
    }

    return invoice_calculate_totals(
        is_array($contract['line_items'] ?? null) ? $contract['line_items'] : [],
        (float)($contract['discount'] ?? 0),
        !empty($contract['optional_printer']),
        is_array($optionalItem) ? $optionalItem : ['unit_price_net' => 0]
    );
}

function invoice_customer_label(string $customerDetails): string
{
    $lines = preg_split('/\R+/', trim($customerDetails)) ?: [];
    foreach ($lines as $line) {
        $line = trim((string)$line);
        if ($line !== '') {
            return $line;
        }
    }

    return 'Customer';
}

/** @return list<string> */
function invoice_customer_field_names(): array
{
    return [
        'customer_contact',
        'customer_company',
        'customer_street',
        'customer_city',
    ];
}

function invoice_build_customer_details_from_post(array $post): string
{
    $lines = [];

    foreach (invoice_customer_field_names() as $field) {
        $value = trim((string)($post[$field] ?? ''));
        if ($value !== '') {
            $lines[] = $value;
        }
    }

    if ($lines !== []) {
        return implode("\n", $lines);
    }

    return trim((string)($post['customer_details'] ?? ''));
}

function collect_invoice_contract_from_post(array $post, string $token, string $mode): array
{
    $config = invoice_contract_mode_config($mode);
    $posType = trim((string)($post['pos_contract_type'] ?? 'karvi_pos'));
    if ($mode === 'pos' && !array_key_exists($posType, invoice_pos_contract_types())) {
        $posType = 'karvi_pos';
    }
    $posConfig = $mode === 'pos' ? invoice_pos_contract_type_config($posType) : null;
    $contractLine = $mode === 'pos'
        ? (string)$posConfig['contract_line']
        : (string)$config['contract_line'];
    $lineItems = invoice_line_items_from_post($post);
    $includeOptional = !empty($post['optional_printer']);
    $discount = invoice_parse_money($post['discount'] ?? 0);
    $optionalItem = is_array($config['optional_item']) ? $config['optional_item'] : ['unit_price_net' => 0];
    if ($mode === 'website' && is_array($config['optional_item'])) {
        $optionalItem = [
            'description' => (string)($config['optional_item']['description'] ?? ''),
            'unit_price_net' => max(0.0, invoice_parse_money(
                $post['optional_printer_price'] ?? ($config['optional_item']['unit_price_net'] ?? 0)
            )),
        ];
    }
    $totals = invoice_calculate_totals(
        $lineItems,
        $discount,
        $mode === 'website' && $includeOptional,
        $optionalItem
    );
    $customerDetails = invoice_build_customer_details_from_post($post);
    $customerContact = trim((string)($post['customer_contact'] ?? ''));
    $customerLabel = invoice_customer_label($customerDetails);
    $contractDate = trim((string)($post['contract_date'] ?? date('Y-m-d')));

    return [
        'token' => $token,
        'sign_token' => $token,
        'status' => 'pending',
        'created_at' => date('Y-m-d H:i:s'),
        'signed_at' => null,
        'pdf_path' => '',
        'contract_line' => $contractLine,
        'type' => match ($mode) {
            'website' => 'website',
            'pos' => 'pos',
            default => 'partner',
        },
        'invoice_mode' => $mode,
        'pos_contract_type' => $mode === 'pos' ? $posType : null,
        'contract_date' => $contractDate,
        'installment_months' => max(1, (int)($post['installment_months'] ?? 12)),
        'customer_details' => $customerDetails,
        'customer_contact' => $customerContact,
        'line_items' => $lineItems,
        'optional_printer' => ($mode === 'website' && $includeOptional) ? 1 : 0,
        'optional_item' => $mode === 'website' ? $optionalItem : null,
        'discount' => $discount,
        'totals' => $totals,
        'delivery_date' => date('Y-m-d', (strtotime($contractDate) ?: time()) + (25 * 86400)),
        'number_of_owners' => '1',
        'owner_names' => [$customerLabel],
        'owner_name' => $customerLabel,
        'restaurant_name' => $customerLabel,
        'partner_name' => $customerLabel,
        'partner_email' => '',
        'email' => '',
        'partnership_type' => $contractLine,
        'partner_type' => $contractLine,
        'brand' => $contractLine,
        'brands' => [],
    ];
}

function validate_invoice_contract_form(array $contract): ?string
{
    if (trim((string)($contract['contract_date'] ?? '')) === '') {
        return 'invoice_error_contract_date';
    }

    if (trim((string)($contract['customer_contact'] ?? '')) === '') {
        return 'invoice_error_customer_details';
    }

    if (empty($contract['line_items']) || !is_array($contract['line_items'])) {
        return 'invoice_error_line_items';
    }

    return null;
}

function invoice_contracts_for_mode(string $mode): array
{
    require_once __DIR__ . '/api_service.php';

    $type = match ($mode) {
        'website' => 'website',
        'pos' => 'pos',
        default => 'partner',
    };
    $lines = invoice_contract_lines_for_mode($mode);
    $contracts = [];

    try {
        if (api_can_use_partner_api()) {
            $contracts = api_fetch_partner_contracts($type);
        } else {
            $contracts = api_all_contracts();
        }
    } catch (Throwable) {
        $contracts = local_store_all_contracts();
    }

    return array_values(array_filter(
        $contracts,
        static fn(array $contract): bool => api_contract_matches_type($contract, $type)
            && in_array(trim((string)($contract['contract_line'] ?? '')), $lines, true)
    ));
}

function invoice_contract_reference(array $contract, string $mode): string
{
    $config = invoice_contract_mode_config($mode);
    $id = (int)($contract['id'] ?? 0);

    return $config['ref_prefix'] . '-' . str_pad((string)$id, 5, '0', STR_PAD_LEFT);
}

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

    $saved = api_save_contract($contract);
    $saved = array_merge($contract, is_array($saved) ? $saved : []);
    $token = (string)($saved['token'] ?? '');

    if ($token === '') {
        throw new RuntimeException('Contract token missing after save.');
    }

    foreach ([
        'line_items',
        'totals',
        'customer_details',
        'invoice_mode',
        'pos_contract_type',
        'contract_line',
        'optional_item',
        'optional_printer',
        'discount',
        'installment_months',
        'delivery_date',
    ] as $field) {
        if (array_key_exists($field, $contract)) {
            $saved[$field] = $contract[$field];
        }
    }

    $offerNumber = invoice_pdf_offer_number($saved);
    $pdfPath = '';
    $pdfFilename = '';

    try {
        require_once __DIR__ . '/invoice_pdf_generator.php';
        $generated = invoice_ensure_contract_pdf($saved);
        if ($generated !== null) {
            $pdfPath = $generated;
            $pdfFilename = basename($generated);
        }
    } catch (Throwable) {
        $pdfPath = '';
        $pdfFilename = '';
    }

    $updated = api_update_contract($token, [
        'pdf_path' => $pdfFilename,
        'offer_number' => $offerNumber,
        'line_items' => $saved['line_items'] ?? [],
        'totals' => $saved['totals'] ?? [],
    ]);

    $merged = is_array($updated)
        ? array_merge($saved, $updated, ['offer_number' => $offerNumber])
        : array_merge($saved, ['offer_number' => $offerNumber]);

    $merged['pdf_path'] = $pdfFilename;

    return $merged;
}

/** @return array{sign_url:string,sign_token:string,customer_link:string,display_link:string,contract_id:int,api_message:string} */
function invoice_contract_signing_links(array $contract): array
{
    require_once __DIR__ . '/api_service.php';

    $signToken = trim((string)($contract['sign_token'] ?? $contract['token'] ?? ''));
    $signUrl = trim((string)($contract['sign_url'] ?? ''));
    if ($signUrl === '' && $signToken !== '' && function_exists('api_partner_sign_url')) {
        $signUrl = api_partner_sign_url($signToken, $contract);
    }

    $customerLink = contract_customer_url($contract);
    $displayLink = contract_public_sign_url_from_record($contract);
    if ($displayLink === '') {
        $displayLink = $customerLink;
    }

    $apiMessage = '';
    if (is_array($contract['partner_api_create_response'] ?? null)) {
        $apiMessage = trim((string)($contract['partner_api_create_response']['message'] ?? ''));
    }

    return [
        'sign_url' => $signUrl,
        'sign_token' => $signToken,
        'customer_link' => $customerLink,
        'display_link' => $displayLink,
        'contract_id' => (int)($contract['id'] ?? $contract['partner_api_id'] ?? 0),
        'api_message' => $apiMessage !== '' ? $apiMessage : 'Contract created.',
    ];
}

function invoice_store_created_contract_session(array $contract, string $mode): void
{
    auth_start();
    $links = invoice_contract_signing_links($contract);
    $_SESSION['invoice_contract_created'] = array_merge($links, [
        'mode' => $mode,
        'message' => $links['api_message'],
        'restaurant_name' => trim((string)($contract['restaurant_name'] ?? $contract['customer_contact'] ?? '')),
    ]);
}

function invoice_contract_pdf_title(array $contract): string
{
    $line = trim((string)($contract['contract_line'] ?? ''));

    return match ($line) {
        'Karvi Website' => 'Karvi Website Contract',
        'Karvi POS' => 'Karvi POS Contract',
        'TSE Lieferando' => 'TSE Lieferando Contract',
        default => 'Karvi Contract',
    };
}
