<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Deal;
use App\Models\Tenant;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Writer\CSV\Options as CsvOptions;
use OpenSpout\Writer\CSV\Writer as CsvWriter;
use OpenSpout\Writer\XLSX\Writer as XlsxWriter;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
 * Export сделок в CSV / XLSX через OpenSpout streaming.
 *
 * Извлечено из DealController (Sprint 3 Phase A, audit O-refactor-01).
 *
 * O-perf-05: streaming устраняет memory pressure. PhpSpreadsheet строил
 * полный объект .xlsx в памяти (для 10K сделок ≈ 100+ MB). OpenSpout пишет
 * в php://output постранично через Writer + Row::fromValues и chunkById(500)
 * по сделкам — пик памяти O(1) от размера экспорта.
 *
 * API контракт сохранён:
 *   POST /api/deals/export {tenant_id, ids[], format?: csv|xlsx}
 *   Headers Content-Type / Content-Disposition без изменений.
 *   CSV: UTF-8 + BOM + ;-разделитель (Excel-friendly RU-локаль).
 *   XLSX: bold-header + auto-size columns.
 *
 * RLS-обёртка SET LOCAL внутри транзакции (PgBouncer-safe). Чужие id
 * отфильтрует where(tenant_id) defense-in-depth.
 */
class DealExportController extends Controller
{
    /** Заголовки таблицы — общие для CSV и XLSX. */
    private const HEADERS = ['ID', 'Имя', 'Телефон', 'Статус', 'Проект ID', 'Менеджер ID', 'Получено'];

    public function export(Request $request): StreamedResponse
    {
        $validated = $request->validate([
            'tenant_id' => 'required|integer|min:1',
            'ids' => 'required|array|min:1|max:10000',
            'ids.*' => 'integer|min:1',
            'format' => 'nullable|string|in:csv,xlsx',
        ]);

        $tenant = Tenant::find($validated['tenant_id']);
        if ($tenant === null) {
            abort(404, 'Тенант не найден.');
        }

        $format = $validated['format'] ?? 'csv';
        $filename = 'deals_export_'.now()->format('Y-m-d').'.'.$format;

        $headers = $format === 'xlsx'
            ? [
                'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
                'Content-Disposition' => 'attachment; filename="'.$filename.'"',
            ]
            : [
                'Content-Type' => 'text/csv; charset=utf-8',
                'Content-Disposition' => 'attachment; filename="'.$filename.'"',
            ];

        return new StreamedResponse(function () use ($validated, $tenant, $format) {
            // RLS-контекст должен быть установлен внутри транзакции на момент
            // фактического SELECT. StreamedResponse callback вызывается уже
            // после Laravel-response pipeline'а, поэтому открываем транзакцию
            // прямо здесь.
            DB::transaction(function () use ($validated, $tenant, $format) {
                DB::statement('SET LOCAL app.current_tenant_id = '.$tenant->id);

                $writer = $this->openWriter($format);
                $writer->openToFile('php://output');

                // Заголовок: для XLSX — bold через Style + имя листа «Сделки».
                // Для CSV — OpenSpout сам пишет UTF-8 BOM (SHOULD_ADD_BOM=true
                // в Options) и `;`-разделитель из конструктора.
                if ($format === 'xlsx') {
                    /** @var XlsxWriter $writer */
                    $writer->getCurrentSheet()->setName('Сделки');
                    $headerStyle = (new Style)->withFontBold(true);
                    $writer->addRow(Row::fromValuesWithStyle(self::HEADERS, $headerStyle));
                } else {
                    $writer->addRow(Row::fromValues(self::HEADERS));
                }

                // chunkById(500) — keyset-friendly; в нашем DealsView это
                // редкий тяжёлый action, экспортировать могут до 10K id.
                Deal::query()
                    ->where('tenant_id', $tenant->id)
                    ->whereIn('id', $validated['ids'])
                    ->orderBy('id')
                    ->chunkById(500, function ($deals) use ($writer) {
                        foreach ($deals as $deal) {
                            /** @var Deal $deal */
                            $writer->addRow(Row::fromValues([
                                $deal->id,
                                (string) ($deal->contact_name ?? ''),
                                (string) $deal->phone,
                                (string) $deal->status,
                                $deal->project_id,
                                $deal->manager_id ?? '',
                                $deal->received_at->toDateTimeString(),
                            ]));
                        }
                    });

                $writer->close();
            });
        }, 200, $headers);
    }

    private function openWriter(string $format): CsvWriter|XlsxWriter
    {
        if ($format === 'xlsx') {
            return new XlsxWriter;
        }

        // CSV: ;-разделитель + UTF-8 BOM (Excel-friendly RU-локаль).
        $options = new CsvOptions(
            FIELD_DELIMITER: ';',
            FIELD_ENCLOSURE: '"',
            SHOULD_ADD_BOM: true,
        );

        return new CsvWriter($options);
    }
}
