<?php

use App\Models\TaskModel;
use App\Services\RecurrenceService;
use CodeIgniter\Exceptions\PageNotFoundException;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\DatabaseTestTrait;
use CodeIgniter\Test\FeatureTestTrait;
use Tests\Support\Auth\AuthTestTrait;
use Tests\Support\Categories\CategoryTestTrait;

/**
 * @internal
 */
final class RecurrenceFlowTest extends CIUnitTestCase
{
    use DatabaseTestTrait;
    use FeatureTestTrait;
    use AuthTestTrait;
    use CategoryTestTrait;

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

    /**
     * @param array<string, mixed> $override
     *
     * @return array<string, mixed>
     */
    private function form(int $categoryId, array $override = []): array
    {
        return array_merge([
            'title'           => 'Repeat me',
            'priority'        => '1',
            'category_id'     => (string) $categoryId,
            'due_date'        => '2026-09-01',
            'due_time'        => '09:00',
            'is_recurring'    => '1',
            'recurrence_type' => 'daily',
            'interval_value'  => '1',
        ], $override);
    }

    public function testCreateRecurringDailyOverHttp(): void
    {
        $user = $this->createUser('rec-http-create@example.com', 1);
        $cat  = $this->createCategory((int) $user['id'], 'Home');

        $this->withSession($this->authSession($user))
            ->post('tasks/create', $this->form((int) $cat['id']))
            ->assertRedirectTo('/dashboard');

        $task = (new TaskModel())->where('user_id', $user['id'])->first();
        $this->assertIsArray($task);
        $this->assertSame(1, (int) $task['is_recurring']);
        $this->assertSame((int) $user['id'], (int) $task['user_id']);
        $this->assertGreaterThan(80, db_connect()->table('task_occurrences')->where('task_id', $task['id'])->where('is_deleted', 0)->countAllResults());
        $this->assertSame(1, db_connect()->table('task_recurrence_rules')->where('task_id', $task['id'])->countAllResults());
    }

    public function testCreateFormShowsRecurrenceControlsAndEditLoadsRule(): void
    {
        $user = $this->createUser('rec-http-form@example.com', 1);
        $cat  = $this->createCategory((int) $user['id'], 'Home');
        $session = $this->authSession($user);

        $page = $this->withSession($session)->get('tasks/create');
        $page->assertOK();
        $page->assertSee('Select to mark as recurring task');
        $page->assertSee('Daily');
        $page->assertSee('Weekly');
        $page->assertSee('Monthly');
        $page->assertSee('required start date');

        $this->withSession($session)->post('tasks/create', $this->form((int) $cat['id'], [
            'recurrence_type' => 'weekly',
            'weekly_days'     => ['1', '3'],
            'interval_value'  => '2',
        ]))->assertRedirectTo('/dashboard');

        $task = (new TaskModel())->where('user_id', $user['id'])->first();
        $edit = $this->withSession($session)->get('tasks/' . $task['id'] . '/edit');
        $edit->assertOK();
        $edit->assertSee('Edit Task');
        $this->assertMatchesRegularExpression('/id="is_recurring"[^>]*checked/', (string) $edit->response()->getBody());
        $this->assertMatchesRegularExpression('/name="recurrence_type" value="weekly"[^>]*checked/', (string) $edit->response()->getBody());
        $edit->assertSee('Monday');
    }

    public function testWeeklyWithoutWeekdayRejectedOverHttp(): void
    {
        $user = $this->createUser('rec-http-week@example.com', 1);
        $cat  = $this->createCategory((int) $user['id'], 'Home');
        $this->withSession($this->authSession($user))
            ->post('tasks/create', $this->form((int) $cat['id'], [
                'recurrence_type' => 'weekly',
            ]))
            ->assertSee(RecurrenceService::MSG_WEEKDAYS);
        $this->assertSame(0, db_connect()->table('tasks')->where('user_id', $user['id'])->countAllResults());
    }

    public function testCannotDeleteAnotherUsersOccurrenceOverHttp(): void
    {
        $alice = $this->createUser('rec-http-a@example.com', 1);
        $bob   = $this->createUser('rec-http-b@example.com', 1);
        $bobCat = $this->createCategory((int) $bob['id'], 'Bob');
        $this->withSession($this->authSession($bob))
            ->post('tasks/create', $this->form((int) $bobCat['id'], ['title' => 'Bob series']))
            ->assertRedirectTo('/dashboard');
        $task = (new TaskModel())->where('user_id', $bob['id'])->first();
        $occ  = db_connect()->table('task_occurrences')->where('task_id', $task['id'])->get()->getRowArray();

        $del = $this->withSession($this->authSession($alice))->post('tasks/occurrences/' . $occ['id'] . '/delete');
        $del->assertStatus(404);
        $this->assertStringNotContainsString('Bob series', (string) $del->response()->getBody());

        $future = $this->withSession($this->authSession($alice))->post('tasks/occurrences/' . $occ['id'] . '/delete-future');
        $future->assertStatus(404);

        $this->assertSame(0, (int) db_connect()->table('task_occurrences')->where('id', $occ['id'])->get()->getRowArray()['is_deleted']);
    }

    public function testDeleteThisAndFutureOverHttp(): void
    {
        $user = $this->createUser('rec-http-del@example.com', 1);
        $cat  = $this->createCategory((int) $user['id'], 'Home');
        $this->withSession($this->authSession($user))
            ->post('tasks/create', $this->form((int) $cat['id']))
            ->assertRedirectTo('/dashboard');
        $task = (new TaskModel())->where('user_id', $user['id'])->first();
        $sep03 = db_connect()->table('task_occurrences')->where('task_id', $task['id'])->where('occurrence_date', '2026-09-03')->get()->getRowArray();
        $this->withSession($this->authSession($user))
            ->post('tasks/occurrences/' . $sep03['id'] . '/delete')
            ->assertOK();
        $this->assertSame(1, (int) db_connect()->table('task_occurrences')->where('id', $sep03['id'])->get()->getRowArray()['is_deleted']);
        $this->assertGreaterThan(
            0,
            db_connect()->table('task_occurrences')->where('task_id', $task['id'])->where('occurrence_date', '2026-09-04')->where('is_deleted', 0)->countAllResults(),
        );

        $sep05 = db_connect()->table('task_occurrences')->where('task_id', $task['id'])->where('occurrence_date', '2026-09-05')->where('is_deleted', 0)->get()->getRowArray();
        $this->withSession($this->authSession($user))
            ->post('tasks/occurrences/' . $sep05['id'] . '/delete-future')
            ->assertOK();

        $this->assertSame(1, (int) db_connect()->table('task_occurrences')->where('id', $sep05['id'])->get()->getRowArray()['is_deleted']);
        $this->assertSame(
            0,
            db_connect()->table('task_occurrences')->where('task_id', $task['id'])->where('occurrence_date', '2026-09-06')->where('is_deleted', 0)->countAllResults(),
        );
        $this->assertGreaterThan(
            0,
            db_connect()->table('task_occurrences')->where('task_id', $task['id'])->where('occurrence_date', '2026-09-04')->where('is_deleted', 0)->countAllResults(),
        );
    }

    public function testGetDoesNotDeleteOccurrences(): void
    {
        $user = $this->createUser('rec-http-get@example.com', 1);
        $cat  = $this->createCategory((int) $user['id'], 'Home');
        $this->withSession($this->authSession($user))
            ->post('tasks/create', $this->form((int) $cat['id'], ['title' => 'Keep series']))
            ->assertRedirectTo('/dashboard');
        $task = (new TaskModel())->where('user_id', $user['id'])->first();
        $occ  = db_connect()->table('task_occurrences')->where('task_id', $task['id'])->get()->getRowArray();

        $this->expectException(PageNotFoundException::class);
        $this->withSession($this->authSession($user))->get('tasks/occurrences/' . $occ['id'] . '/delete');
    }

    public function testAmbiguousDeleteFutureRouteIsNotRegistered(): void
    {
        $routes = (string) file_get_contents(APPPATH . 'Config/Routes.php');
        $this->assertStringNotContainsString("post('tasks/(:num)/delete-future'", $routes);
        $this->assertStringContainsString("post('tasks/occurrences/(:num)/delete-future'", $routes);

        $user = $this->createUser('rec-http-route@example.com', 1);
        $cat  = $this->createCategory((int) $user['id'], 'Home');
        $this->withSession($this->authSession($user))
            ->post('tasks/create', $this->form((int) $cat['id'], ['title' => 'Keep route']))
            ->assertRedirectTo('/dashboard');
        $task = (new TaskModel())->where('user_id', $user['id'])->first();
        $occ  = db_connect()->table('task_occurrences')->where('task_id', $task['id'])->get()->getRowArray();

        $this->expectException(PageNotFoundException::class);
        $this->withSession($this->authSession($user))->get('tasks/occurrences/' . $occ['id'] . '/complete');
    }
}
