<?php

namespace App\Services\Reimbursements;

use App\Models\ReimbursementBatch;
use Illuminate\Support\Facades\DB;
use RuntimeException;

class ReimbursementExportService
{
    public function __construct(private ReimbursementRecipientService $recipients)
    {
    }

    public function buildUnderProcessingCsv(): array
    {
        $builder = DB::table('expenses')
            ->select('expenses.*', 'u.firstname as purchaser_firstname', 'u.lastname as purchaser_lastname')
            ->leftJoin('users as u', 'u.id', '=', 'expenses.purchased_by')
            ->where('expenses.status', 'approved')
            ->whereNull('expenses.reimbursement_id')
            ->where('expenses.category', '!=', 'Donation')
            ->orderByDesc('expenses.created_at');

        $records = $builder->get();
        $grouped = [];

        foreach ($records as $row) {
            $personId = (int) ($row->purchased_by ?? 0);
            $name = trim(($row->purchaser_firstname ?? '') . ' ' . ($row->purchaser_lastname ?? ''));
            if ($name === '') {
                $name = $row->purchaser_firstname ?? $row->purchaser_lastname ?? 'Unknown';
            }
            $key = $personId > 0 ? 'id_' . $personId : 'name_' . strtolower($name);
            if (!isset($grouped[$key])) {
                $grouped[$key] = [
                    'name' => $name ?: 'Unknown',
                    'total' => 0.0,
                    'vendors' => [],
                    'descriptions' => [],
                ];
            }

            $amount = (float) ($row->amount ?? 0);
            $grouped[$key]['total'] += $amount;

            $vendor = trim((string) ($row->retailor ?? ''));
            if ($vendor !== '' && !in_array($vendor, $grouped[$key]['vendors'], true)) {
                $grouped[$key]['vendors'][] = $vendor;
            }

            $desc = trim((string) ($row->description ?? ''));
            if ($desc !== '' && !in_array($desc, $grouped[$key]['descriptions'], true)) {
                $grouped[$key]['descriptions'][] = $desc;
            }
        }

        $rows = [];
        $rows[] = ['Person', 'Total Amount', 'Vendors', 'Descriptions'];
        foreach ($grouped as $entry) {
            $rows[] = [
                $entry['name'],
                number_format($entry['total'], 2, '.', ''),
                implode('; ', $entry['vendors']),
                implode('; ', $entry['descriptions']),
            ];
        }

        return [
            'filename' => 'under_processing_expenses.csv',
            'rows' => $rows,
        ];
    }

    public function buildProcessedCsv(array $filters): array
    {
        $builder = DB::table('reimbursements')
            ->select('reimbursements.*', 'u.firstname as reimb_firstname', 'u.lastname as reimb_lastname')
            ->leftJoin('users as u', 'u.id', '=', 'reimbursements.reimbursed_to');

        if (!empty($filters['semester'])) {
            $builder->where('reimbursements.semester', $filters['semester']);
        }
        if (!empty($filters['status'])) {
            $builder->where('reimbursements.status', $filters['status']);
        }
        if (!empty($filters['user_id'])) {
            $builder->where('reimbursements.reimbursed_to', $filters['user_id']);
        }
        if (!empty($filters['school_year'])) {
            $builder->where('reimbursements.school_year', $filters['school_year']);
        }

        $records = $builder->orderByDesc('reimbursements.created_at')->get();
        $rows = [];
        $rows[] = ['Amount', 'Reimbursed To', 'Status', 'Method', 'Check #', 'School Year', 'Semester', 'Date'];

        foreach ($records as $row) {
            $hasName = trim(($row->reimb_firstname ?? '') . ' ' . ($row->reimb_lastname ?? '')) !== '';
            if (!$hasName) {
                $label = $this->recipients->specialRecipientLabel($row->reimbursed_to ?? null);
                if ($label !== null) {
                    $row->reimb_firstname = $label;
                    $row->reimb_lastname = '';
                }
            }

            $rows[] = [
                $row->amount,
                trim(($row->reimb_firstname ?? '') . ' ' . ($row->reimb_lastname ?? '')),
                $row->status,
                $row->reimbursement_method ?? '-',
                $row->check_number ?? '-',
                $row->school_year ?? '-',
                $row->semester ?? '-',
                $row->created_at,
            ];
        }

        return [
            'filename' => 'reimbursements_export.csv',
            'rows' => $rows,
        ];
    }

    public function buildBatchCsv(int $batchId): array
    {
        $batch = ReimbursementBatch::query()->find($batchId);
        if (!$batch) {
            throw new RuntimeException('Batch not found.');
        }

        $batchLabel = trim((string) ($batch->title ?? '')) ?: ('Batch #' . $batchId);
        $receiptRows = $this->fetchBatchReceiptRows($batchId);

        $rows = [];
        $this->writeBatchCsvMetadata($rows, $batchLabel, $batch->toArray());
        $this->writeBatchCsvSummary($rows, $receiptRows, $this->formatCsvDate($batch->closed_at ?? null));

        return [
            'filename' => sprintf('batch_%d_items.csv', $batchId),
            'rows' => $rows,
        ];
    }

    private function fetchBatchReceiptRows(int $batchId, array $receiptIds = []): array
    {
        $builder = DB::table('reimbursement_batch_items as bi')
            ->select('e.id as expense_id', 'e.retailor', 'e.amount', 'e.description', 'e.receipt_path as receipt_filename', 'e.date_of_purchase as purchase_date', 'u.firstname as purchaser_firstname', 'u.lastname as purchaser_lastname')
            ->join('expenses as e', 'e.id', '=', 'bi.expense_id')
            ->leftJoin('users as u', 'u.id', '=', 'e.purchased_by')
            ->where('bi.batch_id', $batchId);

        if (!empty($receiptIds)) {
            $builder->whereIn('e.id', $receiptIds);
        }

        $rows = $builder->get()->toArray();

        return array_values(array_filter($rows, static function ($row) {
            return !empty($row->receipt_filename);
        }));
    }

    private function writeBatchCsvMetadata(array &$rows, string $batchLabel, array $batch): void
    {
        $closedAtRaw = $batch['closed_at'] ?? null;
        $rows[] = ['Batch Title', $batchLabel];
        $rows[] = ['School Year', $batch['school_year'] ?? '', 'Semester', $batch['semester'] ?? '', 'Closed At', $this->formatCsvDate($closedAtRaw)];
        $rows[] = [];
        $rows[] = ['Amount', 'Credit', 'Paid', 'Balance', 'Date', 'By Who', 'Vendor', 'Description'];
    }

    private function writeBatchCsvSummary(array &$rows, array $receiptRows, string $totalsDate = ''): void
    {
        $receiptTotalsByRecipient = [];
        foreach ($receiptRows as $row) {
            $recipientName = trim(($row->purchaser_firstname ?? '') . ' ' . ($row->purchaser_lastname ?? ''));
            if ($recipientName === '') {
                $recipientName = 'Unknown';
            }
            $vendor = trim((string) ($row->retailor ?? ''));
            if ($vendor === '') {
                $vendor = 'Vendor N/A';
            }
            $dateText = $this->formatCsvDate($row->purchase_date ?? null);
            $descriptionValue = trim((string) ($row->description ?? ''));
            $descriptionParts = $descriptionValue !== '' ? [$descriptionValue] : ['Receipt'];
            $amountValue = (float) ($row->amount ?? 0);
            $receiptTotalsByRecipient[$recipientName] = ($receiptTotalsByRecipient[$recipientName] ?? 0.0) + $amountValue;
            $rows[] = [
                number_format($amountValue, 2, '.', ''),
                '',
                number_format($amountValue, 2, '.', ''),
                '0.00',
                $dateText,
                $recipientName,
                $vendor,
                implode(' | ', $descriptionParts),
            ];
        }

        if (!empty($receiptTotalsByRecipient)) {
            $rows[] = [];
            $rows[] = ['Totals by Recipient', '', '', '', '', '', '', ''];
            foreach ($receiptTotalsByRecipient as $recipient => $total) {
                $formatted = number_format($total, 2, '.', '');
                $rows[] = [
                    $formatted,
                    '',
                    $formatted,
                    '0.00',
                    $totalsDate,
                    $recipient,
                    '',
                    '',
                ];
            }
        }
    }

    private function formatCsvDate(?string $value): string
    {
        if (!$value) {
            return '';
        }
        $timestamp = strtotime($value);
        if ($timestamp === false) {
            return '';
        }
        return date('m-d-Y', $timestamp);
    }
}
