<?php

namespace Tests\Unit\AdOps;

use App\Services\AdOps\CampaignCsv;
use PHPUnit\Framework\TestCase;

/**
 * Unit tests for CampaignCsv::decodeRow edge cases that were flagged in the
 * AP-29/AP-30 review. These are pure functions with no CI4/DB/HTTP dependency,
 * so they run without a framework bootstrap.
 */
final class CampaignCsvTest extends TestCase
{
    public function testEmptyPipeListColumnDoesNotEmitSingleBlankElement(): void
    {
        // Regression: explode('|', '') returned [''] and the API received
        // {"targeting":{"browsers":[""]}}, overwriting valid targeting.
        $row = ['name' => 'X', 'targeting_browsers' => ''];
        $payload = CampaignCsv::decodeRow($row);

        $this->assertArrayNotHasKey('browsers', $payload['targeting'] ?? []);
    }

    public function testPipeListColumnTrimsAndFiltersEmpties(): void
    {
        $row = ['name' => 'X', 'targeting_browsers' => 'chrome | safari||firefox|'];
        $payload = CampaignCsv::decodeRow($row);

        $this->assertSame(['chrome', 'safari', 'firefox'], $payload['targeting']['browsers']);
    }

    public function testEmptyTargetingScheduleProducesNoScheduleEntries(): void
    {
        // Regression: isset('') was true, so an empty schedule column pushed a
        // schedule entry with all-false fields into the PATCH payload.
        $row = ['name' => 'X', 'targeting_schedule' => ''];
        $payload = CampaignCsv::decodeRow($row);

        $this->assertArrayNotHasKey('schedule', $payload['targeting'] ?? []);
    }

    public function testValidScheduleEntryIsParsedCorrectly(): void
    {
        $row = ['name' => 'X', 'targeting_schedule' => 'mon 09:00-17:00|tue 10:00-18:00'];
        $payload = CampaignCsv::decodeRow($row);

        $this->assertSame(
            [
                ['day' => 'mon', 'start_time' => '09:00', 'end_time' => '17:00'],
                ['day' => 'tue', 'start_time' => '10:00', 'end_time' => '18:00'],
            ],
            $payload['targeting']['schedule']
        );
    }

    public function testMalformedScheduleEntryIsSkipped(): void
    {
        // Regression: the regex produced an all-false entry when it didn't match.
        $row = ['name' => 'X', 'targeting_schedule' => 'garbage|mon 09:00-17:00'];
        $payload = CampaignCsv::decodeRow($row);

        $this->assertCount(1, $payload['targeting']['schedule']);
        $this->assertSame('mon', $payload['targeting']['schedule'][0]['day']);
    }

    public function testLogicValueTokenWithEmptyValueIsSkipped(): void
    {
        // Regression: "include:" (logic-only) set categories.include = false,
        // which the API serialized as a literal boolean.
        $row = ['name' => 'X', 'targeting_categories' => 'include:'];
        $payload = CampaignCsv::decodeRow($row);

        $this->assertArrayNotHasKey('categories', $payload['targeting'] ?? []);
    }

    public function testLogicValueTokenWithEmptyLogicIsSkipped(): void
    {
        // ":value" would have set categories[''] — an empty-string key.
        $row = ['name' => 'X', 'targeting_categories' => ':1|2|3'];
        $payload = CampaignCsv::decodeRow($row);

        $this->assertArrayNotHasKey('categories', $payload['targeting'] ?? []);
    }

    public function testLogicValueTokenDoubleColonIsSkipped(): void
    {
        $row = ['name' => 'X', 'targeting_categories' => '::'];
        $payload = CampaignCsv::decodeRow($row);

        $this->assertArrayNotHasKey('categories', $payload['targeting'] ?? []);
    }

    public function testLogicValueTokenValidParsesIntoNestedArray(): void
    {
        $row = ['name' => 'X', 'targeting_categories' => 'include:1|2|3'];
        $payload = CampaignCsv::decodeRow($row);

        $this->assertSame(['1', '2', '3'], $payload['targeting']['categories']['include']);
    }

    public function testRetargetingBothHalvesRequiredToEmit(): void
    {
        foreach ([':', 'include:', ':1|2', ''] as $bad) {
            $payload = CampaignCsv::decodeRow(['name' => 'X', 'targeting_retargeting' => $bad]);
            $this->assertArrayNotHasKey(
                'retargeting',
                $payload['targeting'] ?? [],
                'Expected retargeting to be absent for input: ' . var_export($bad, true)
            );
        }
    }

    public function testExcludePipeListEmptyValueDoesNotEmit(): void
    {
        $row = ['name' => 'X', 'targeting_exclude_states' => ''];
        $payload = CampaignCsv::decodeRow($row);

        $this->assertArrayNotHasKey('exclude', $payload['targeting'] ?? []);
    }
}
