<?php

namespace Tests\Feature\Api;

use App\Services\AttendanceCommentTemplateService;
use Illuminate\Foundation\Testing\WithFaker;
use Mockery;
use Tests\TestCase;

class AttendanceCommentTemplateApiTest extends TestCase
{
    use WithFaker;

    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }

    public function test_can_list_templates(): void
    {
        $mock = Mockery::mock(AttendanceCommentTemplateService::class);
        $mock->shouldReceive('list')
            ->once()
            ->with(false)
            ->andReturn([
                [
                    'id' => 1,
                    'min_score' => 0,
                    'max_score' => 59,
                    'template_text' => 'Needs improvement',
                    'is_active' => true,
                ],
                [
                    'id' => 2,
                    'min_score' => 60,
                    'max_score' => 79,
                    'template_text' => 'Good effort',
                    'is_active' => true,
                ],
            ]);

        $this->app->instance(AttendanceCommentTemplateService::class, $mock);

        $response = $this->getJson('/api/attendance-comment-templates');

        $response->assertOk()
            ->assertJson([
                'status' => 'success',
            ])
            ->assertJsonCount(2, 'data')
            ->assertJsonPath('data.0.id', 1)
            ->assertJsonPath('data.1.max_score', 79);
    }

    public function test_can_list_templates_active_only(): void
    {
        $mock = Mockery::mock(AttendanceCommentTemplateService::class);
        $mock->shouldReceive('list')
            ->once()
            ->with(true)
            ->andReturn([]);

        $this->app->instance(AttendanceCommentTemplateService::class, $mock);

        $response = $this->getJson('/api/attendance-comment-templates?active_only=1');

        $response->assertOk()
            ->assertJson([
                'status' => 'success',
                'data' => [],
            ]);
    }

    public function test_can_create_template(): void
    {
        $payload = [
            'min_score' => 80,
            'max_score' => 100,
            'template_text' => 'Excellent attendance and punctuality.',
            'is_active' => true,
        ];

        $mock = Mockery::mock(AttendanceCommentTemplateService::class);
        $mock->shouldReceive('create')
            ->once()
            ->with(Mockery::on(function ($data) use ($payload) {
                return (int)$data['min_score'] === 80
                    && (int)$data['max_score'] === 100
                    && $data['template_text'] === $payload['template_text']
                    && (bool)$data['is_active'] === true;
            }))
            ->andReturn([
                'id' => 10,
                'min_score' => 80,
                'max_score' => 100,
                'template_text' => 'Excellent attendance and punctuality.',
                'is_active' => true,
            ]);

        $this->app->instance(AttendanceCommentTemplateService::class, $mock);

        $response = $this->postJson('/api/attendance-comment-templates', $payload);

        $response->assertCreated()
            ->assertJsonPath('status', 'success')
            ->assertJsonPath('data.id', 10)
            ->assertJsonPath('data.min_score', 80)
            ->assertJsonPath('data.max_score', 100);
    }

    public function test_store_validation_fails_when_max_less_than_min(): void
    {
        $payload = [
            'min_score' => 90,
            'max_score' => 70,
            'template_text' => 'Invalid range',
            'is_active' => true,
        ];

        $response = $this->postJson('/api/attendance-comment-templates', $payload);

        $response->assertStatus(422)
            ->assertJsonValidationErrors(['max_score']);
    }

    public function test_can_show_template(): void
    {
        $mock = Mockery::mock(AttendanceCommentTemplateService::class);
        $mock->shouldReceive('findOrFail')
            ->once()
            ->with(5)
            ->andReturn([
                'id' => 5,
                'min_score' => 60,
                'max_score' => 69,
                'template_text' => 'Average attendance',
                'is_active' => true,
            ]);

        $this->app->instance(AttendanceCommentTemplateService::class, $mock);

        $response = $this->getJson('/api/attendance-comment-templates/5');

        $response->assertOk()
            ->assertJsonPath('status', 'success')
            ->assertJsonPath('data.id', 5);
    }

    public function test_can_update_template(): void
    {
        $payload = [
            'template_text' => 'Updated comment text',
            'is_active' => false,
        ];

        $mock = Mockery::mock(AttendanceCommentTemplateService::class);
        $mock->shouldReceive('update')
            ->once()
            ->with(7, Mockery::on(function ($data) {
                return $data['template_text'] === 'Updated comment text'
                    && (bool)$data['is_active'] === false;
            }))
            ->andReturn([
                'id' => 7,
                'min_score' => 70,
                'max_score' => 79,
                'template_text' => 'Updated comment text',
                'is_active' => false,
            ]);

        $this->app->instance(AttendanceCommentTemplateService::class, $mock);

        $response = $this->putJson('/api/attendance-comment-templates/7', $payload);

        $response->assertOk()
            ->assertJsonPath('status', 'success')
            ->assertJsonPath('data.id', 7)
            ->assertJsonPath('data.is_active', false);
    }

    public function test_update_validation_fails_if_both_scores_invalid_relation(): void
    {
        $payload = [
            'min_score' => 50,
            'max_score' => 20,
        ];

        $response = $this->putJson('/api/attendance-comment-templates/7', $payload);

        $response->assertStatus(422)
            ->assertJsonValidationErrors(['max_score']);
    }

    public function test_can_delete_template(): void
    {
        $mock = Mockery::mock(AttendanceCommentTemplateService::class);
        $mock->shouldReceive('delete')
            ->once()
            ->with(12);

        $this->app->instance(AttendanceCommentTemplateService::class, $mock);

        $response = $this->deleteJson('/api/attendance-comment-templates/12');

        $response->assertOk()
            ->assertJsonPath('status', 'success')
            ->assertJsonPath('message', 'Template deleted successfully.');
    }

    public function test_legacy_list_data_endpoint_returns_templates_key(): void
    {
        $mock = Mockery::mock(AttendanceCommentTemplateService::class);
        $mock->shouldReceive('list')
            ->once()
            ->with(false)
            ->andReturn([
                [
                    'id' => 1,
                    'min_score' => 0,
                    'max_score' => 49,
                    'template_text' => 'Low attendance',
                    'is_active' => true,
                ]
            ]);

        $this->app->instance(AttendanceCommentTemplateService::class, $mock);

        $response = $this->getJson('/api/attendance-comment-templates/list-data');

        $response->assertOk()
            ->assertJsonCount(1, 'templates')
            ->assertJsonPath('templates.0.id', 1);
    }
}