<?php

use App\Database\Seeds\DatabaseSeeder;
use App\Database\Seeds\SupportCategorySeeder;
use App\Models\UserModel;
use App\Services\ProfileService;
use App\Services\SupportService;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\DatabaseTestTrait;
use Tests\Support\Auth\AuthTestTrait;

/**
 * @internal
 */
final class SupportServiceTest extends CIUnitTestCase
{
    use DatabaseTestTrait;
    use AuthTestTrait;

    protected $namespace = 'App';
    protected $refresh   = true;
    protected $migrate   = true;
    protected $seed      = '';

    private SupportService $support;

    protected function setUp(): void
    {
        parent::setUp();
        $this->seed(SupportCategorySeeder::class);
        $this->support = new SupportService();
    }

    /**
     * @param array<string, mixed> $fields
     *
     * @return array<string, mixed>
     */
    private function withProfile(array $user, array $fields): array
    {
        (new UserModel())->skipValidation(true)->update($user['id'], $fields);
        $saved = (new UserModel())->find($user['id']);
        $this->assertIsArray($saved);

        return $saved;
    }

    private function categoryId(string $title = 'Technical Issue'): int
    {
        $row = db_connect()->table('support_categories')->where('title', $title)->get()->getRowArray();
        $this->assertIsArray($row);

        return (int) $row['id'];
    }

    /**
     * @param array<string, string> $override
     *
     * @return array<string, string>
     */
    private function payload(array $override = []): array
    {
        return array_merge([
            'profile_name'        => 'Casey',
            'email'               => 'attacker@example.com',
            'phone'               => '',
            'support_category_id' => (string) $this->categoryId(),
            'message'             => 'The dashboard filter is stuck.',
            'user_id'             => '0',
        ], $override);
    }

    public function testApprovedCategoriesLoadFromDatabaseInSeedOrder(): void
    {
        $titles = array_map(
            static fn (array $row): string => (string) $row['title'],
            $this->support->activeCategories(),
        );

        $this->assertSame(SupportService::APPROVED_TITLES, $titles);
        $this->assertSame(0, db_connect()->table('categories')->countAllResults());
    }

    public function testDatabaseSeederDoesNotSeedTaskCategories(): void
    {
        $this->seed(DatabaseSeeder::class);

        $titles = array_column(
            db_connect()->table('support_categories')->orderBy('sort_order', 'ASC')->orderBy('id', 'ASC')->get()->getResultArray(),
            'title',
        );
        $this->assertSame(SupportService::APPROVED_TITLES, $titles);
        $this->assertSame(0, db_connect()->table('categories')->countAllResults());
        $this->assertNotContains('Home', $titles);
        $this->assertNotContains('Office', $titles);

        $seeder = (string) file_get_contents(APPPATH . 'Database/Seeds/DatabaseSeeder.php');
        $this->assertStringContainsString('SupportCategorySeeder', $seeder);
        $this->assertSame(1, substr_count($seeder, '->call('));
    }

    public function testMissingAndInvalidCategoriesAreRejected(): void
    {
        $user = $this->createUser('support-svc-cat@example.com', 1);

        $missing = $this->support->submit((int) $user['id'], $this->payload([
            'support_category_id' => '',
        ]));
        $this->assertFalse($missing['ok']);
        $this->assertSame(SupportService::MSG_CATEGORY, $missing['errors']['support_category_id']);

        $bogus = $this->support->submit((int) $user['id'], $this->payload([
            'support_category_id' => '999999',
        ]));
        $this->assertFalse($bogus['ok']);
        $this->assertSame(SupportService::MSG_CATEGORY_INVALID, $bogus['errors']['support_category_id']);

        $inactiveId = $this->categoryId('Other');
        db_connect()->table('support_categories')->where('id', $inactiveId)->update(['is_active' => 0]);
        $inactive = $this->support->submit((int) $user['id'], $this->payload([
            'support_category_id' => (string) $inactiveId,
        ]));
        $this->assertFalse($inactive['ok']);
        $this->assertSame(SupportService::MSG_CATEGORY_INVALID, $inactive['errors']['support_category_id']);
        $this->assertSame(0, db_connect()->table('support_requests')->countAllResults());
    }

    public function testEmptyCategoriesFailSafelyWithoutInsert(): void
    {
        db_connect()->table('support_categories')->update(['is_active' => 0]);
        $user = $this->createUser('support-svc-empty@example.com', 1);

        $result = $this->support->submit((int) $user['id'], $this->payload([
            'support_category_id' => '1',
        ]));

        $this->assertFalse($result['ok']);
        $this->assertSame(SupportService::MSG_NO_CATEGORIES, $result['errors']['form']);
        $this->assertSame(SupportService::MSG_NO_CATEGORIES, $result['errors']['support_category_id']);
        $this->assertSame([], $this->support->activeCategories());
        $this->assertSame(0, db_connect()->table('support_requests')->countAllResults());
    }

    public function testEditableNameIsRequiredAndWhitespaceRejected(): void
    {
        $user = $this->createUser('support-svc-name@example.com', 1);

        $blank = $this->support->submit((int) $user['id'], $this->payload(['profile_name' => '']));
        $this->assertFalse($blank['ok']);
        $this->assertSame(SupportService::MSG_NAME_REQUIRED, $blank['errors']['profile_name']);

        $spaces = $this->support->submit((int) $user['id'], $this->payload(['profile_name' => "  \t  "]));
        $this->assertFalse($spaces['ok']);
        $this->assertSame(SupportService::MSG_NAME_REQUIRED, $spaces['errors']['profile_name']);
        $this->assertSame(0, db_connect()->table('support_requests')->countAllResults());
    }

    public function testPhoneValidationMatchesProfileRules(): void
    {
        $user = $this->createUser('support-svc-phone@example.com', 1);

        $invalid = $this->support->submit((int) $user['id'], $this->payload(['phone' => 'call-me']));
        $this->assertFalse($invalid['ok']);
        $this->assertSame(ProfileService::MSG_PHONE_INVALID, $invalid['errors']['phone']);

        $ok = $this->support->submit((int) $user['id'], $this->payload(['phone' => '']));
        $this->assertTrue($ok['ok'] ?? false, (string) ($ok['message'] ?? 'blank phone should be allowed'));
        $this->seeInDatabase('support_requests', [
            'user_id' => $user['id'],
            'phone'   => null,
        ]);
    }

    public function testMessageRequiredTrimmedAndCapped(): void
    {
        $user = $this->createUser('support-svc-msg@example.com', 1);

        $blank = $this->support->submit((int) $user['id'], $this->payload(['message' => '']));
        $this->assertFalse($blank['ok']);
        $this->assertSame(SupportService::MSG_MESSAGE, $blank['errors']['message']);

        $spaces = $this->support->submit((int) $user['id'], $this->payload(['message' => "  \n\t  "]));
        $this->assertFalse($spaces['ok']);
        $this->assertSame(SupportService::MSG_MESSAGE, $spaces['errors']['message']);

        $tooLong = $this->support->submit((int) $user['id'], $this->payload([
            'message' => str_repeat('a', SupportService::MESSAGE_MAX + 1),
        ]));
        $this->assertFalse($tooLong['ok']);
        $this->assertSame(SupportService::MSG_MESSAGE_LENGTH, $tooLong['errors']['message']);

        $ok = $this->support->submit((int) $user['id'], $this->payload([
            'message' => "Please help.\nThe list will not refresh.",
        ]));
        $this->assertTrue($ok['ok'] ?? false);
        $this->seeInDatabase('support_requests', [
            'user_id' => $user['id'],
            'message' => "Please help.\nThe list will not refresh.",
        ]);
    }

    public function testSnapshotsUseServerTruthAndIgnorePostedIdentity(): void
    {
        $user = $this->withProfile($this->createUser('actual@example.com', 1), [
            'profile_name' => 'Dana Saved',
            'phone'        => '+1 202 555 0100',
        ]);
        $bob = $this->createUser('other-user@example.com', 1);

        $result = $this->support->submit((int) $user['id'], $this->payload([
            'profile_name'        => 'Attacker Name',
            'email'               => 'attacker@example.com',
            'phone'               => '+44 20 7946 0958',
            'user_id'             => (string) $bob['id'],
            'support_category_id' => (string) $this->categoryId('Feedback/Suggestion'),
            'message'             => 'Need help with notifications.',
        ]));

        $this->assertTrue($result['ok'] ?? false, (string) ($result['message'] ?? 'submit failed'));
        $this->seeInDatabase('support_requests', [
            'user_id'             => $user['id'],
            'profile_name'        => 'Dana Saved',
            'email'               => 'actual@example.com',
            'phone'               => '+1 202 555 0100',
            'support_category_id' => $this->categoryId('Feedback/Suggestion'),
            'message'             => 'Need help with notifications.',
            'status'              => 'open',
        ]);
        $this->dontSeeInDatabase('support_requests', ['user_id' => $bob['id']]);
        $this->dontSeeInDatabase('support_requests', ['email' => 'attacker@example.com']);
        $this->seeInDatabase('users', [
            'id'           => $user['id'],
            'profile_name' => 'Dana Saved',
            'phone'        => '+1 202 555 0100',
            'email'        => 'actual@example.com',
        ]);
    }

    public function testSupportOnlyNameAndPhoneDoNotUpdateProfile(): void
    {
        $user = $this->createUser('snapshot-only@example.com', 1);

        $result = $this->support->submit((int) $user['id'], $this->payload([
            'profile_name' => 'Support Only',
            'phone'        => '+91 555 010 1234',
            'message'      => 'Account question.',
        ]));

        $this->assertTrue($result['ok'] ?? false, (string) ($result['message'] ?? 'submit failed'));
        $this->seeInDatabase('support_requests', [
            'user_id'      => $user['id'],
            'profile_name' => 'Support Only',
            'email'        => 'snapshot-only@example.com',
            'phone'        => '+91 555 010 1234',
        ]);
        $saved = (new UserModel())->find($user['id']);
        $this->assertIsArray($saved);
        $this->assertTrue($saved['profile_name'] === null || $saved['profile_name'] === '');
        $this->assertTrue($saved['phone'] === null || $saved['phone'] === '');
        $this->assertSame('snapshot-only@example.com', $saved['email']);
    }
}
