<?php

namespace Tests\Feature;

use App\Exports\SegmentContractsExport;
use App\Http\Controllers\SegmentController;
use App\Http\Requests\ExportSegmentContractsRequest;
use App\Models\Client;
use App\Models\Contract;
use App\Models\Person\Person as PersonModel;
use App\Models\Segment;
use App\Models\User;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

class SegmentExportTest extends \Tests\TestCase
{
    public function test_exports_only_current_page_when_requested(): void
    {
        Excel::fake();
        Carbon::setTestNow('2025-12-10 12:00:00');

        $segment = Segment::factory()->create();
        $contracts = Contract::factory()->count(2)->create();

        foreach ($contracts as $contract) {
            DB::table('contract_segment')->insert([
                'contract_id' => $contract->id,
                'segment_id' => $segment->id,
                'active' => true,
                'created_at' => now(),
                'updated_at' => now(),
            ]);
        }

        $user = User::factory()->create();
        $request = $this->makeExportRequest([
            'scope' => 'current',
            'columns' => ['reference', 'client'],
            'page' => 1,
            'per_page' => 1,
        ], $user);

        $response = app(SegmentController::class)->export($request, $segment);

        $this->assertInstanceOf(BinaryFileResponse::class, $response);

        $expectedName = $this->expectedFilename($segment);

        Excel::assertDownloaded($expectedName, function (SegmentContractsExport $export) {
            $this->assertSame(1, $export->query()->get()->count());

            return true;
        });
    }

    public function test_exports_full_segment_when_scope_all(): void
    {
        Excel::fake();
        Carbon::setTestNow('2025-12-10 12:05:00');

        $segment = Segment::factory()->create();
        $contracts = Contract::factory()->count(3)->create();

        foreach ($contracts as $contract) {
            DB::table('contract_segment')->insert([
                'contract_id' => $contract->id,
                'segment_id' => $segment->id,
                'active' => true,
                'created_at' => now(),
                'updated_at' => now(),
            ]);
        }

        $user = User::factory()->create();
        $request = $this->makeExportRequest([
            'scope' => 'all',
            'columns' => SegmentContractsExport::allowedColumns(),
        ], $user);

        $response = app(SegmentController::class)->export($request, $segment);

        $this->assertInstanceOf(BinaryFileResponse::class, $response);

        $request = $this->makeExportRequest([
            'scope' => 'all',
            'columns' => SegmentContractsExport::allowedColumns(),
        ], $user);

        $expectedName = $this->expectedFilename($segment);

        Excel::assertDownloaded($expectedName, function (SegmentContractsExport $export) use ($contracts) {
            $this->assertSame($contracts->count(), $export->query()->get()->count());

            return true;
        });
    }

    public function test_export_filename_includes_client_name_when_filtered(): void
    {
        Excel::fake();
        Carbon::setTestNow('2025-12-10 12:10:00');

        $segment = Segment::factory()->create(['name' => 'VIP Segment']);
        $contract = Contract::factory()->create();

        DB::table('contract_segment')->insert([
            'contract_id' => $contract->id,
            'segment_id' => $segment->id,
            'active' => true,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $client = Client::factory()
            ->for(PersonModel::factory(['full_name' => 'Ana Novak']), 'person')
            ->create();

        $contract->clientCase?->update(['client_id' => $client->id]);

        $user = User::factory()->create();
        $request = $this->makeExportRequest([
            'scope' => 'all',
            'columns' => ['reference'],
            'client' => (string) $client->uuid,
        ], $user);

        $expectedName = $this->expectedFilename($segment, 'Ana Novak');

        $response = app(SegmentController::class)->export($request, $segment);

        $this->assertInstanceOf(BinaryFileResponse::class, $response);

        Excel::assertDownloaded($expectedName, function (SegmentContractsExport $export) {
            $this->assertSame(1, $export->query()->get()->count());

            return true;
        });
    }

    public function test_column_formats_apply_to_reference_and_date_columns(): void
    {
        $export = new SegmentContractsExport(
            Contract::query(),
            ['reference', 'start_date', 'client', 'end_date']
        );

        $this->assertSame(
            [
                'A' => SegmentContractsExport::TEXT_EXCEL_FORMAT,
                'B' => SegmentContractsExport::DATE_EXCEL_FORMAT,
                'D' => SegmentContractsExport::DATE_EXCEL_FORMAT,
            ],
            $export->columnFormats()
        );
    }

    public function test_date_values_are_converted_to_excel_serial_numbers(): void
    {
        $contract = Contract::factory()->make([
            'start_date' => '2025-10-30',
        ]);

        $export = new SegmentContractsExport(
            Contract::query(),
            ['start_date']
        );

        $row = $export->map($contract);

        $this->assertIsFloat($row[0]);
        $this->assertGreaterThan(40000, $row[0]);
    }

    private function makeExportRequest(array $payload, User $user): ExportSegmentContractsRequest
    {
        $request = ExportSegmentContractsRequest::create('/segments/export', 'POST', $payload);
        $request->setContainer($this->app);
        $request->setRedirector($this->app->make(Redirector::class));
        $request->setUserResolver(fn () => $user);
        $request->setRouteResolver(fn () => (object) [
            'parameter' => fn () => null,
        ]);
        $request->validateResolved();

        return $request;
    }

    private function expectedFilename(Segment $segment, ?string $clientName = null): string
    {
        $base = now()->format('dmy').'_'.$this->slugify($segment->name).'-Pogodbe';

        if ($clientName) {
            return sprintf('%s_%s.xlsx', $base, $this->slugify($clientName));
        }

        return sprintf('%s.xlsx', $base);
    }

    private function slugify(string $value): string
    {
        $slug = trim(preg_replace('/[^a-zA-Z0-9]+/', '-', $value), '-');

        return $slug !== '' ? $slug : 'data';
    }
}
