<?php
declare(strict_types=1);

namespace App\Modules\Accounting;

use App\Core\Http\Request;
use App\Core\Http\Response;
use App\Core\View\Template;
use App\Core\I18n\Translator;
use App\Core\Security\Csrf;
use App\Modules\Auth\AuthService;
use App\Modules\Settings\ReceiptConfigRepository;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

final class AccountingController
{
    public function __construct(
        private readonly Template $template,
        private readonly Translator $translator,
        private readonly AuthService $auth,
        private readonly ReceiptRepository $receipts,
        private readonly ReceiptConfigRepository $receiptConfigs
    ) {
    }

    public function index(Request $request): Response
    {
        $filters = $this->parseFilters($request);
        $result = $this->receipts->paginate($filters);

        $totalPages = max(1, (int) ceil(((int) $result['total']) / max(1, (int) $result['per_page'])));

        $configs = array_filter(
            $this->receiptConfigs->listAll(),
            static fn(array $c): bool => (int) ($c['is_active'] ?? 0) === 1
        );
        $configOptions = ['' => $this->translator->get('accounting.filters.any')];
        foreach ($configs as $cfg) {
            $configOptions[(string) ($cfg['id'] ?? '')] = (string) ($cfg['name'] ?? '');
        }

        $tableRows = array_map(fn(array $row): array => $this->toTableRow($row), (array) ($result['items'] ?? []));

        $html = $this->template->render('accounting/index', [
            'title' => $this->translator->get('accounting.title'),
            'activeMenu' => 'accounting',
            'user' => $this->auth->user(),
            'tableList' => [
                'list_key' => 'accounting',
                'base_path' => '/accounting',
                'query' => $filters,
                'filters' => [
                    [
                        'key' => 'search',
                        'label' => $this->translator->get('accounting.filters.search'),
                        'type' => 'text',
                        'value' => $filters['search'],
                    ],
                    [
                        'key' => 'config_id',
                        'label' => $this->translator->get('accounting.filters.config'),
                        'type' => 'select',
                        'value' => (string) $filters['config_id'],
                        'options' => $configOptions,
                    ],
                    [
                        'key' => 'date_from',
                        'label' => $this->translator->get('accounting.filters.date_from'),
                        'type' => 'date',
                        'value' => $filters['date_from'],
                    ],
                    [
                        'key' => 'date_to',
                        'label' => $this->translator->get('accounting.filters.date_to'),
                        'type' => 'date',
                        'value' => $filters['date_to'],
                    ],
                ],
                'columns' => [
                    ['key' => 'receipt_number', 'label' => $this->translator->get('accounting.columns.number'), 'sortable' => true, 'sort_key' => 'receipt_number', 'raw' => true],
                    ['key' => 'issue_date', 'label' => $this->translator->get('accounting.columns.issue_date'), 'sortable' => true, 'sort_key' => 'issue_date'],
                    ['key' => 'sale_date', 'label' => $this->translator->get('accounting.columns.sale_date')],
                    ['key' => 'total_gross', 'label' => $this->translator->get('accounting.columns.total_gross'), 'sortable' => true, 'sort_key' => 'total_gross'],
                    ['key' => 'config_name', 'label' => $this->translator->get('accounting.columns.config')],
                    ['key' => 'order_ref', 'label' => $this->translator->get('accounting.columns.order'), 'raw' => true],
                ],
                'rows' => $tableRows,
                'pagination' => [
                    'page' => (int) ($result['page'] ?? 1),
                    'total_pages' => $totalPages,
                    'total' => (int) ($result['total'] ?? 0),
                    'per_page' => (int) ($result['per_page'] ?? 20),
                ],
                'per_page_options' => [20, 50, 100],
                'empty_message' => $this->translator->get('accounting.empty'),
                'show_actions' => false,
                'selectable' => true,
                'select_name' => 'selected_ids[]',
                'select_value_key' => 'id',
            ],
            'exportQuery' => $this->buildExportQuery($filters),
            'csrfToken' => Csrf::token(),
        ], 'layouts/app');

        return Response::html($html);
    }

    public function export(Request $request): Response
    {
        if (!Csrf::validate((string) $request->input('_token', ''))) {
            return Response::redirect('/accounting');
        }

        $exportAll = (string) $request->input('export_all', '0') === '1';
        $selectedIds = (array) $request->input('selected_ids', []);
        $selectedIds = array_filter(array_map('intval', $selectedIds), static fn(int $id): bool => $id > 0);

        $filters = $this->parseFilters($request);

        if ($exportAll) {
            $rows = $this->receipts->exportData($filters);
        } elseif ($selectedIds !== []) {
            $rows = $this->receipts->findByIds($selectedIds);
        } else {
            return Response::redirect('/accounting');
        }

        $spreadsheet = new Spreadsheet();
        $sheet = $spreadsheet->getActiveSheet();
        $sheet->setTitle('Paragony');

        $headers = ['Numer', 'Data wystawienia', 'Data sprzedazy', 'Kwota brutto', 'Konfiguracja', 'Nr zamowienia', 'Nr referencyjny'];
        foreach ($headers as $col => $header) {
            $sheet->setCellValue([$col + 1, 1], $header);
        }

        $sheet->getStyle('1:1')->getFont()->setBold(true);

        $rowNum = 2;
        foreach ($rows as $row) {
            $sheet->setCellValue([1, $rowNum], (string) ($row['receipt_number'] ?? ''));
            $issueDateRaw = (string) ($row['issue_date'] ?? '');
            $sheet->setCellValue([2, $rowNum], strlen($issueDateRaw) >= 16 ? substr($issueDateRaw, 0, 16) : $issueDateRaw);
            $sheet->setCellValue([3, $rowNum], (string) ($row['sale_date'] ?? ''));
            $sheet->setCellValue([4, $rowNum], (float) ($row['total_gross'] ?? 0));
            $sheet->setCellValue([5, $rowNum], (string) ($row['config_name'] ?? ''));
            $sheet->setCellValue([6, $rowNum], (string) ($row['internal_order_number'] ?? $row['external_order_id'] ?? ''));
            $sheet->setCellValue([7, $rowNum], (string) ($row['order_reference_value'] ?? ''));
            $rowNum++;
        }

        foreach (range(1, 7) as $col) {
            $sheet->getColumnDimensionByColumn($col)->setAutoSize(true);
        }

        $writer = new Xlsx($spreadsheet);
        ob_start();
        $writer->save('php://output');
        $content = ob_get_clean();

        $filename = 'paragony_' . date('Y-m-d') . '.xlsx';

        return new Response($content ?: '', 200, [
            'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'Content-Disposition' => 'attachment; filename="' . $filename . '"',
        ]);
    }

    /**
     * @return array<string, mixed>
     */
    private function parseFilters(Request $request): array
    {
        return [
            'search' => trim((string) $request->input('search', '')),
            'config_id' => (int) $request->input('config_id', 0),
            'date_from' => trim((string) $request->input('date_from', '')),
            'date_to' => trim((string) $request->input('date_to', '')),
            'sort' => (string) $request->input('sort', 'issue_date'),
            'sort_dir' => (string) $request->input('sort_dir', 'DESC'),
            'page' => max(1, (int) $request->input('page', 1)),
            'per_page' => max(1, min(100, (int) $request->input('per_page', 20))),
        ];
    }

    /**
     * @param array<string, mixed> $row
     * @return array<string, mixed>
     */
    private function toTableRow(array $row): array
    {
        $orderId = (int) ($row['order_id'] ?? 0);
        $receiptId = (int) ($row['id'] ?? 0);
        $receiptNumber = htmlspecialchars((string) ($row['receipt_number'] ?? ''), ENT_QUOTES, 'UTF-8');
        $orderNumber = htmlspecialchars(
            (string) ($row['internal_order_number'] ?? $row['external_order_id'] ?? '#' . $orderId),
            ENT_QUOTES,
            'UTF-8'
        );

        return [
            'id' => (string) $receiptId,
            'receipt_number' => '<a href="/orders/' . $orderId . '/receipt/' . $receiptId . '">' . $receiptNumber . '</a>',
            'issue_date' => isset($row['issue_date']) && strlen((string) $row['issue_date']) >= 16
                ? substr((string) $row['issue_date'], 0, 16)
                : (string) ($row['issue_date'] ?? ''),
            'sale_date' => (string) ($row['sale_date'] ?? ''),
            'total_gross' => $row['total_gross'] !== null
                ? number_format((float) $row['total_gross'], 2, '.', ' ')
                : '-',
            'config_name' => (string) ($row['config_name'] ?? '-'),
            'order_ref' => '<a href="/orders/' . $orderId . '">' . $orderNumber . '</a>',
        ];
    }

    /**
     * @param array<string, mixed> $filters
     */
    private function buildExportQuery(array $filters): string
    {
        $params = [];
        if (($filters['search'] ?? '') !== '') {
            $params['search'] = (string) $filters['search'];
        }
        if (($filters['config_id'] ?? 0) > 0) {
            $params['config_id'] = (string) $filters['config_id'];
        }
        if (($filters['date_from'] ?? '') !== '') {
            $params['date_from'] = (string) $filters['date_from'];
        }
        if (($filters['date_to'] ?? '') !== '') {
            $params['date_to'] = (string) $filters['date_to'];
        }

        return $params !== [] ? '?' . http_build_query($params) : '';
    }
}
