<?php

namespace Tests\Unit;

use App\Models\Pay;
use App\Models\Pay_details;
use App\Models\Sale;
use App\Services\ImagesServices;
use App\Services\PaysServices;
use App\Services\UserBalanceServices;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
use ReflectionClass;
use Tests\TestCase;

class PaysServicesTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();

        Schema::dropIfExists('user_balance');
        Schema::dropIfExists('activity_log');
        Schema::dropIfExists('pay');
        Schema::dropIfExists('pay_type');
        Schema::dropIfExists('bank');
        Schema::dropIfExists('users');
        Schema::dropIfExists('people');
        Schema::dropIfExists('pay_details');
        Schema::dropIfExists('sale');

        Schema::create('people', function (Blueprint $table): void {
            $table->id();
            $table->string('last_name')->nullable();
            $table->string('phone')->nullable();
            $table->string('document_number')->nullable();
            $table->timestamps();
        });

        Schema::create('users', function (Blueprint $table): void {
            $table->id();
            $table->unsignedBigInteger('people_id')->nullable();
            $table->string('name')->nullable();
            $table->string('email')->nullable();
            $table->string('password')->nullable();
            $table->tinyInteger('is_active')->default(1);
            $table->timestamps();
        });

        Schema::create('bank', function (Blueprint $table): void {
            $table->id();
            $table->string('name')->nullable();
            $table->timestamps();
        });

        Schema::create('pay_type', function (Blueprint $table): void {
            $table->id();
            $table->unsignedBigInteger('company_id')->nullable();
            $table->string('name')->nullable();
            $table->tinyInteger('is_active')->default(1);
            $table->timestamps();
        });

        Schema::create('pay', function (Blueprint $table): void {
            $table->id();
            $table->unsignedBigInteger('pay_type_id');
            $table->unsignedBigInteger('company_id')->nullable();
            $table->unsignedBigInteger('user_id');
            $table->date('pay_date');
            $table->decimal('amount', 10, 2)->default(0);
            $table->string('id_card_number')->nullable();
            $table->unsignedBigInteger('bank_id')->nullable();
            $table->string('reference')->nullable();
            $table->string('status')->default('Pendiente');
            $table->string('url_pay_imagen')->nullable();
            $table->tinyInteger('is_full_input')->default(0);
            $table->string('level_input')->nullable();
            $table->timestamps();
        });

        Schema::create('sale', function (Blueprint $table): void {
            $table->id();
            $table->decimal('total', 10, 2)->default(0);
            $table->decimal('debt', 10, 2)->default(0);
            $table->decimal('aux_debt', 10, 2)->default(0);
            $table->decimal('amount_rate', 10, 4)->default(1);
            $table->string('status')->default('pendiente');
            $table->timestamps();
        });

        Schema::create('pay_details', function (Blueprint $table): void {
            $table->id();
            $table->unsignedBigInteger('pay_id');
            $table->unsignedBigInteger('sale_id');
            $table->decimal('amount', 10, 2)->default(0);
            $table->timestamps();
        });

        Schema::create('user_balance', function (Blueprint $table): void {
            $table->id();
            $table->unsignedBigInteger('user_id');
            $table->unsignedBigInteger('pay_id')->nullable();
            $table->decimal('balance', 10, 2)->default(0);
            $table->tinyInteger('is_active')->default(1);
            $table->timestamps();
        });

        Schema::create('activity_log', function (Blueprint $table): void {
            $table->id();
            $table->string('log_name')->nullable();
            $table->text('description');
            $table->nullableMorphs('subject');
            $table->nullableMorphs('causer');
            $table->string('event')->nullable();
            $table->string('batch_uuid')->nullable();
            $table->text('properties')->nullable();
            $table->unsignedBigInteger('company_id')->nullable();
            $table->timestamps();
        });
    }

    protected function tearDown(): void
    {
        Schema::dropIfExists('activity_log');
        Schema::dropIfExists('user_balance');
        Schema::dropIfExists('pay');
        Schema::dropIfExists('pay_type');
        Schema::dropIfExists('bank');
        Schema::dropIfExists('users');
        Schema::dropIfExists('people');
        Schema::dropIfExists('pay_details');
        Schema::dropIfExists('sale');

        parent::tearDown();
    }

    public function test_close_pay_bill_updates_debt_and_aux_debt(): void
    {
        $sale = Sale::create([
            'debt' => 100.00,
            'aux_debt' => 50.00,
            'amount_rate' => 2.00,
            'status' => 'pendiente',
        ]);

        Pay_details::create([
            'pay_id' => 1,
            'sale_id' => $sale->id,
            'amount' => 40.00,
        ]);

        $service = new PaysServices(
            $this->createMock(ImagesServices::class),
            $this->createMock(UserBalanceServices::class)
        );

        $pay = new Pay();
        $pay->id = 1;

        $service->closePayBill($pay);

        $sale->refresh();

        $this->assertSame(60.00, (float) $sale->debt);
        $this->assertSame(30.00, (float) $sale->aux_debt);
        $this->assertSame('pendiente', $sale->status);
    }

    public function test_close_pay_bill_marks_sale_completed_when_debt_reaches_zero(): void
    {
        $sale = Sale::create([
            'debt' => 40.00,
            'aux_debt' => 20.00,
            'amount_rate' => 2.00,
            'status' => 'pendiente',
        ]);

        Pay_details::create([
            'pay_id' => 2,
            'sale_id' => $sale->id,
            'amount' => 50.00,
        ]);

        $service = new PaysServices(
            $this->createMock(ImagesServices::class),
            $this->createMock(UserBalanceServices::class)
        );

        $pay = new Pay();
        $pay->id = 2;

        $service->closePayBill($pay);

        $sale->refresh();

        $this->assertSame(0.00, (float) $sale->debt);
        $this->assertSame(0.00, (float) $sale->aux_debt);
        $this->assertSame('completada', $sale->status);
    }

    public function test_normalize_decimal_input_accepts_localized_formats(): void
    {
        $service = new PaysServices(
            $this->createMock(ImagesServices::class),
            $this->createMock(UserBalanceServices::class)
        );

        $reflection = new ReflectionClass($service);
        $method = $reflection->getMethod('normalizeDecimalInput');
        $method->setAccessible(true);

        $this->assertSame('1234.56', $method->invoke($service, '1.234,56'));
        $this->assertSame('1234.56', $method->invoke($service, '1,234.56'));
        $this->assertSame('1234.56', $method->invoke($service, '1234,56'));
    }

    public function test_update_pay_from_aprobado_to_rechazado_reverts_sale_debt_and_user_balance(): void
    {
        $personId = (int) \DB::table('people')->insertGetId([
            'last_name' => 'Tester',
            'phone' => '0000',
            'document_number' => 'DOC-1',
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $userId = (int) \DB::table('users')->insertGetId([
            'people_id' => $personId,
            'name' => 'QA User',
            'email' => 'qa@example.com',
            'password' => 'secret',
            'is_active' => 1,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $payTypeId = (int) \DB::table('pay_type')->insertGetId([
            'company_id' => 1,
            'name' => 'Transferencia',
            'is_active' => 1,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $sale = Sale::create([
            'total' => 100.00,
            'debt' => 60.00,
            'aux_debt' => 30.00,
            'amount_rate' => 2.00,
            'status' => 'completada',
        ]);

        $payId = (int) \DB::table('pay')->insertGetId([
            'pay_type_id' => $payTypeId,
            'company_id' => 1,
            'user_id' => $userId,
            'pay_date' => now()->toDateString(),
            'amount' => 100.00,
            'id_card_number' => null,
            'bank_id' => null,
            'reference' => 'REF-1',
            'status' => 'Aprobado',
            'url_pay_imagen' => null,
            'is_full_input' => 0,
            'level_input' => null,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        Pay_details::create([
            'pay_id' => $payId,
            'sale_id' => $sale->id,
            'amount' => 40.00,
        ]);

        \DB::table('user_balance')->insert([
            'user_id' => $userId,
            'pay_id' => $payId,
            'balance' => 80.00,
            'is_active' => 1,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $service = new PaysServices(
            $this->createMock(ImagesServices::class),
            new UserBalanceServices()
        );

        $request = new Request([
            'pay_type_id' => $payTypeId,
            'pay_date' => now()->toDateString(),
            'amount' => 100.00,
            'id_card_number' => null,
            'bank_id' => null,
            'reference' => 'REF-1',
            'status' => 'Rechazado',
        ]);

        $service->updatePay($request, $payId);

        $sale->refresh();
        $this->assertSame(100.00, (float) $sale->debt);
        $this->assertSame(50.00, (float) $sale->aux_debt);
        $this->assertSame('pendiente', $sale->status);

        $userBalance = \DB::table('user_balance')->where('user_id', $userId)->first();
        $this->assertNotNull($userBalance);
        $this->assertSame(20.00, (float) $userBalance->balance);
        $this->assertSame(1, (int) $userBalance->is_active);
    }

    public function test_update_pay_from_rechazado_to_aprobado_applies_sale_debt_and_creates_balance(): void
    {
        $personId = (int) \DB::table('people')->insertGetId([
            'last_name' => 'Tester',
            'phone' => '0000',
            'document_number' => 'DOC-2',
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $userId = (int) \DB::table('users')->insertGetId([
            'people_id' => $personId,
            'name' => 'QA User 2',
            'email' => 'qa2@example.com',
            'password' => 'secret',
            'is_active' => 1,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $payTypeId = (int) \DB::table('pay_type')->insertGetId([
            'company_id' => 1,
            'name' => 'Transferencia',
            'is_active' => 1,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $sale = Sale::create([
            'total' => 100.00,
            'debt' => 100.00,
            'aux_debt' => 50.00,
            'amount_rate' => 2.00,
            'status' => 'pendiente',
        ]);

        $payId = (int) \DB::table('pay')->insertGetId([
            'pay_type_id' => $payTypeId,
            'company_id' => 1,
            'user_id' => $userId,
            'pay_date' => now()->toDateString(),
            'amount' => 100.00,
            'id_card_number' => null,
            'bank_id' => null,
            'reference' => 'REF-2',
            'status' => 'Rechazado',
            'url_pay_imagen' => null,
            'is_full_input' => 0,
            'level_input' => null,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        Pay_details::create([
            'pay_id' => $payId,
            'sale_id' => $sale->id,
            'amount' => 40.00,
        ]);

        $service = new PaysServices(
            $this->createMock(ImagesServices::class),
            new UserBalanceServices()
        );

        $request = new Request([
            'pay_type_id' => $payTypeId,
            'pay_date' => now()->toDateString(),
            'amount' => 100.00,
            'id_card_number' => null,
            'bank_id' => null,
            'reference' => 'REF-2',
            'status' => 'Aprobado',
        ]);

        $service->updatePay($request, $payId);

        $sale->refresh();
        $this->assertSame(60.00, (float) $sale->debt);
        $this->assertSame(30.00, (float) $sale->aux_debt);
        $this->assertSame('pendiente', $sale->status);

        $userBalance = \DB::table('user_balance')->where('user_id', $userId)->first();
        $this->assertNotNull($userBalance);
        $this->assertSame((int) $payId, (int) $userBalance->pay_id);
        $this->assertSame(60.00, (float) $userBalance->balance);
        $this->assertSame(1, (int) $userBalance->is_active);
    }
}
