<?php

namespace App\Services;

class Gstr1ExcelExporter
{
    /**
     * @param  array<string, mixed>  $report
     */
    public function download(array $report, string $filename): \Symfony\Component\HttpFoundation\StreamedResponse
    {
        $xml = $this->buildSpreadsheetXml($report);

        return response()->streamDownload(function () use ($xml) {
            echo $xml;
        }, $filename, [
            'Content-Type' => 'application/vnd.ms-excel',
            'Cache-Control' => 'no-store, no-cache, must-revalidate, max-age=0',
        ]);
    }

    /**
     * @param  array<string, mixed>  $report
     */
    private function buildSpreadsheetXml(array $report): string
    {
        $company = $report['company'];
        $sheets = [
            'help' => $this->helpSheet($company, $report),
            'b2b' => $this->dataSheet(
                config('reports.gstr1.b2b_columns'),
                array_map(fn ($r) => [
                    $r['gstin'], $r['name'], $r['number'], $r['date'], $r['value'],
                    $r['pos'], $r['rchrg'], $r['inv_type'], $r['rate'], $r['taxable'], $r['cess'],
                ], $report['b2b'])
            ),
            'b2cs' => $this->dataSheet(
                config('reports.gstr1.b2cs_columns'),
                array_map(fn ($r) => [
                    $r['type'], $r['pos'], $r['rate'], $r['taxable'], $r['cess'], $r['ecom'],
                ], $report['b2cs'])
            ),
            'cdnr' => $this->dataSheet(
                config('reports.gstr1.cdnr_columns'),
                array_map(fn ($r) => [
                    $r['gstin'], $r['name'], $r['number'], $r['date'], $r['note_type'],
                    $r['pos'], $r['rchrg'], $r['supply_type'], $r['rate'], $r['taxable'], $r['cess'],
                ], $report['cdnr'])
            ),
            'hsn' => $this->dataSheet(
                config('reports.gstr1.hsn_columns'),
                array_map(fn ($r) => [
                    $r['hsn'], $r['desc'], $r['uqc'], $r['qty'], $r['total'],
                    $r['taxable'], $r['igst'], $r['cgst'], $r['sgst'], $r['cess'],
                ], $report['hsn'])
            ),
            'docs' => $this->dataSheet(
                ['Document Type', 'Count'],
                [
                    ['Taxable invoices', $report['docs']['invoices'] ?? 0],
                    ['Credit notes', $report['docs']['credit_notes'] ?? 0],
                ]
            ),
        ];

        $out = '<?xml version="1.0" encoding="UTF-8"?>'."\n";
        $out .= '<?mso-application progid="Excel.Sheet"?>'."\n";
        $out .= '<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" '
            .'xmlns:o="urn:schemas-microsoft-com:office:office" '
            .'xmlns:x="urn:schemas-microsoft-com:office:excel" '
            .'xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">'."\n";
        $out .= '<Styles>'
            .'<Style ss:ID="Header"><Font ss:Bold="1"/><Interior ss:Color="#E8E8E8" ss:Pattern="Solid"/></Style>'
            .'</Styles>'."\n";

        foreach ($sheets as $name => $body) {
            $out .= '<Worksheet ss:Name="'.$this->escapeXml($name).'">'."\n";
            $out .= '<Table>'.$body.'</Table>'."\n";
            $out .= '</Worksheet>'."\n";
        }

        $out .= '</Workbook>';

        return $out;
    }

    /**
     * @param  list<string>  $headers
     * @param  list<list<mixed>>  $rows
     */
    private function dataSheet(array $headers, array $rows): string
    {
        $xml = '';
        $xml .= $this->row($headers, true);
        foreach ($rows as $row) {
            $xml .= $this->row($row, false);
        }

        return $xml;
    }

    /**
     * @param  list<mixed>  $cells
     */
    private function row(array $cells, bool $header): string
    {
        $style = $header ? ' ss:StyleID="Header"' : '';
        $xml = '<Row>';
        foreach ($cells as $cell) {
            $type = is_numeric($cell) && ! is_string($cell) ? 'Number' : 'String';
            $val = $cell ?? '';
            if ($type === 'Number') {
                $xml .= '<Cell'.$style.'><Data ss:Type="Number">'.$val.'</Data></Cell>';
            } else {
                $xml .= '<Cell'.$style.'><Data ss:Type="String">'.$this->escapeXml((string) $val).'</Data></Cell>';
            }
        }
        $xml .= '</Row>';

        return $xml;
    }

    /**
     * @param  array<string, mixed>  $report
     */
    private function helpSheet($company, array $report): string
    {
        $lines = [
            ['GSTR-1 Report'],
            ['Company', $company->name ?? ''],
            ['GSTIN', $report['gstin'] ?? ''],
            ['Period', $report['period_from'].' to '.$report['period_to']],
            ['Generated', now()->format('d-m-Y H:i')],
            [],
            ['Use Export JSON for GST offline tool upload.'],
            ['Shipping charges are included as line items per invoice.'],
        ];

        $xml = '';
        foreach ($lines as $line) {
            $xml .= $this->row($line, false);
        }

        return $xml;
    }

    private function escapeXml(string $value): string
    {
        return htmlspecialchars($value, ENT_XML1 | ENT_QUOTES, 'UTF-8');
    }
}
