<?php

declare(strict_types=1);

namespace App\Services\Reports;

use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;

class ReportExportService
{
    /**
     * Map of report types to their Blade view names.
     *
     * @var array<string, string>
     */
    private const VIEW_MAP = [
        'revenue' => 'reports.revenue',
        'profit_loss' => 'reports.profit_loss',
        'tax' => 'reports.tax',
        'aging' => 'reports.aging',
        'refund' => 'reports.refund',
        'subscription' => 'reports.subscription',
    ];

    /**
     * Export report as PDF using DomPDF.
     *
     * @param  array<string, mixed>  $data
     * @param  array<string, mixed>  $meta
     */
    public function toPdf(string $reportType, array $data, array $meta): Response
    {
        $view = self::VIEW_MAP[$reportType] ?? 'reports.revenue';

        $pdf = Pdf::loadView($view, [
            'data' => $data,
            'meta' => $meta,
        ]);

        $filename = $this->generateFilename($reportType, $meta, 'pdf');

        return $pdf->download($filename);
    }

    /**
     * Export report as CSV.
     *
     * @param  array<string, mixed>  $data
     * @param  array<string, mixed>  $meta
     */
    public function toCsv(string $reportType, array $data, array $meta): StreamedResponse
    {
        $filename = $this->generateFilename($reportType, $meta, 'csv');

        return new StreamedResponse(function () use ($reportType, $data, $meta): void {
            $handle = fopen('php://output', 'w');
            if ($handle === false) {
                return;
            }

            // Header row with report info
            fputcsv($handle, ['Report', $meta['title'] ?? ucfirst(str_replace('_', ' ', $reportType))]);
            if (! empty($meta['start_date'])) {
                fputcsv($handle, ['Date Range', ($meta['start_date'] ?? '').' to '.($meta['end_date'] ?? '')]);
            }
            fputcsv($handle, ['Generated', $meta['generated_at'] ?? now()->toIso8601String()]);
            fputcsv($handle, []);

            $this->writeCsvData($handle, $reportType, $data);

            fclose($handle);
        }, 200, [
            'Content-Type' => 'text/csv',
            'Content-Disposition' => "attachment; filename=\"{$filename}\"",
        ]);
    }

    /**
     * Export report as JSON.
     *
     * @param  array<string, mixed>  $data
     * @param  array<string, mixed>  $meta
     */
    public function toJson(string $reportType, array $data, array $meta): JsonResponse
    {
        return response()->json([
            'report_type' => $reportType,
            'meta' => $meta,
            'data' => $data,
        ]);
    }

    /**
     * Generate a filename for the report export.
     *
     * @param  array<string, mixed>  $meta
     */
    private function generateFilename(string $reportType, array $meta, string $extension): string
    {
        $parts = ['ezscale', str_replace('_', '-', $reportType), 'report'];

        if (! empty($meta['start_date'])) {
            $parts[] = $meta['start_date'];
            $parts[] = 'to';
            $parts[] = $meta['end_date'] ?? 'now';
        }

        return implode('-', $parts).'.'.$extension;
    }

    /**
     * Write report-specific CSV data rows.
     *
     * @param  resource  $handle
     * @param  array<string, mixed>  $data
     */
    private function writeCsvData($handle, string $reportType, array $data): void
    {
        match ($reportType) {
            'revenue' => $this->writeCsvRevenue($handle, $data),
            'profit_loss' => $this->writeCsvProfitLoss($handle, $data),
            'tax' => $this->writeCsvTax($handle, $data),
            'aging' => $this->writeCsvAging($handle, $data),
            'refund' => $this->writeCsvRefund($handle, $data),
            'subscription' => $this->writeCsvSubscription($handle, $data),
            default => null,
        };
    }

    /**
     * @param  resource  $handle
     * @param  array<string, mixed>  $data
     */
    private function writeCsvRevenue($handle, array $data): void
    {
        fputcsv($handle, ['Total Revenue', number_format($data['total_revenue'] ?? 0, 2)]);
        fputcsv($handle, []);

        fputcsv($handle, ['Revenue by Period']);
        fputcsv($handle, ['Period', 'Amount']);
        foreach ($data['by_period'] ?? [] as $row) {
            fputcsv($handle, [$row['period'], number_format($row['amount'], 2)]);
        }
        fputcsv($handle, []);

        fputcsv($handle, ['Revenue by Service Type']);
        fputcsv($handle, ['Type', 'Amount']);
        foreach ($data['by_service_type'] ?? [] as $row) {
            fputcsv($handle, [$row['type'], number_format($row['amount'], 2)]);
        }
        fputcsv($handle, []);

        fputcsv($handle, ['Revenue by Gateway']);
        fputcsv($handle, ['Gateway', 'Amount']);
        foreach ($data['by_gateway'] ?? [] as $row) {
            fputcsv($handle, [$row['gateway'], number_format($row['amount'], 2)]);
        }
        fputcsv($handle, []);

        fputcsv($handle, ['Revenue by Plan']);
        fputcsv($handle, ['Plan', 'Amount']);
        foreach ($data['by_plan'] ?? [] as $row) {
            fputcsv($handle, [$row['plan'], number_format($row['amount'], 2)]);
        }
    }

    /**
     * @param  resource  $handle
     * @param  array<string, mixed>  $data
     */
    private function writeCsvProfitLoss($handle, array $data): void
    {
        fputcsv($handle, ['Item', 'Amount']);
        fputcsv($handle, ['Revenue', number_format($data['revenue'] ?? 0, 2)]);
        fputcsv($handle, ['Refunds', number_format($data['refunds'] ?? 0, 2)]);
        fputcsv($handle, ['Gateway Fees', number_format($data['gateway_fees'] ?? 0, 2)]);
        fputcsv($handle, ['Infrastructure Cost', number_format($data['infrastructure_cost'] ?? 0, 2)]);
        fputcsv($handle, []);
        fputcsv($handle, ['Net Profit', number_format($data['net_profit'] ?? 0, 2)]);
        fputcsv($handle, ['Margin %', ($data['margin_percentage'] ?? 0).'%']);
    }

    /**
     * @param  resource  $handle
     * @param  array<string, mixed>  $data
     */
    private function writeCsvTax($handle, array $data): void
    {
        fputcsv($handle, ['Total Tax Collected', number_format($data['total_tax'] ?? 0, 2)]);
        fputcsv($handle, []);

        fputcsv($handle, ['Tax by Country']);
        fputcsv($handle, ['Country', 'Amount', 'Invoice Count']);
        foreach ($data['by_country'] ?? [] as $row) {
            fputcsv($handle, [$row['country'], number_format($row['amount'], 2), $row['invoice_count']]);
        }
        fputcsv($handle, []);

        fputcsv($handle, ['Tax by Region']);
        fputcsv($handle, ['Region', 'Country', 'Amount']);
        foreach ($data['by_region'] ?? [] as $row) {
            fputcsv($handle, [$row['region'], $row['country'], number_format($row['amount'], 2)]);
        }
    }

    /**
     * @param  resource  $handle
     * @param  array<string, mixed>  $data
     */
    private function writeCsvAging($handle, array $data): void
    {
        fputcsv($handle, ['Aging Summary']);
        fputcsv($handle, ['Bracket', 'Count', 'Amount']);
        fputcsv($handle, ['0-30 days', $data['current']['count'] ?? 0, number_format($data['current']['amount'] ?? 0, 2)]);
        fputcsv($handle, ['31-60 days', $data['days_31_60']['count'] ?? 0, number_format($data['days_31_60']['amount'] ?? 0, 2)]);
        fputcsv($handle, ['61-90 days', $data['days_61_90']['count'] ?? 0, number_format($data['days_61_90']['amount'] ?? 0, 2)]);
        fputcsv($handle, ['90+ days', $data['days_90_plus']['count'] ?? 0, number_format($data['days_90_plus']['amount'] ?? 0, 2)]);
        fputcsv($handle, []);
        fputcsv($handle, ['Total', $data['total']['count'] ?? 0, number_format($data['total']['amount'] ?? 0, 2)]);
        fputcsv($handle, []);

        fputcsv($handle, ['Invoice Details']);
        fputcsv($handle, ['Invoice #', 'Customer', 'Amount', 'Due Date', 'Days Overdue']);
        foreach ($data['invoices'] ?? [] as $inv) {
            fputcsv($handle, [$inv['invoice_number'], $inv['customer'], number_format($inv['amount'], 2), $inv['due_date'], $inv['days_overdue']]);
        }
    }

    /**
     * @param  resource  $handle
     * @param  array<string, mixed>  $data
     */
    private function writeCsvRefund($handle, array $data): void
    {
        fputcsv($handle, ['Total Refunds', number_format($data['total_refunds'] ?? 0, 2)]);
        fputcsv($handle, ['Refund Count', $data['refund_count'] ?? 0]);
        fputcsv($handle, []);

        fputcsv($handle, ['Refunds by Gateway']);
        fputcsv($handle, ['Gateway', 'Amount', 'Count']);
        foreach ($data['by_gateway'] ?? [] as $row) {
            fputcsv($handle, [$row['gateway'], number_format($row['amount'], 2), $row['count']]);
        }
        fputcsv($handle, []);

        fputcsv($handle, ['Refunds by Month']);
        fputcsv($handle, ['Month', 'Amount', 'Count']);
        foreach ($data['by_month'] ?? [] as $row) {
            fputcsv($handle, [$row['month'], number_format($row['amount'], 2), $row['count']]);
        }
    }

    /**
     * @param  resource  $handle
     * @param  array<string, mixed>  $data
     */
    private function writeCsvSubscription($handle, array $data): void
    {
        fputcsv($handle, ['Metric', 'Value']);
        fputcsv($handle, ['New Subscriptions', $data['new_subscriptions'] ?? 0]);
        fputcsv($handle, ['Cancelled Subscriptions', $data['cancelled_subscriptions'] ?? 0]);
        fputcsv($handle, ['Churn Rate', ($data['churn_rate'] ?? 0).'%']);
        fputcsv($handle, ['MRR Start', number_format($data['mrr_start'] ?? 0, 2)]);
        fputcsv($handle, ['MRR End', number_format($data['mrr_end'] ?? 0, 2)]);
        fputcsv($handle, ['MRR Change', number_format($data['mrr_change'] ?? 0, 2)]);
        fputcsv($handle, []);

        fputcsv($handle, ['Breakdown by Plan']);
        fputcsv($handle, ['Plan', 'New', 'Cancelled', 'Active']);
        foreach ($data['by_plan'] ?? [] as $row) {
            fputcsv($handle, [$row['plan'], $row['new'], $row['cancelled'], $row['active']]);
        }
    }
}
