<?php

namespace Tests\Feature;

use App\Models\Kategori;
use App\Models\ObatKeluar;
use App\Models\ObatMasuk;
use App\Models\Resep;
use App\Models\ResepItem;
use App\Models\Satuan;
use App\Models\User;
use Tests\DatabaseTestCase;

class ResepTest extends DatabaseTestCase
{
    private User $dokter;
    private User $apoteker;
    private ObatMasuk $obatMasuk;

    protected function setUp(): void
    {
        parent::setUp();

        $this->dokter   = User::factory()->create(['role' => 'dokter']);
        $this->apoteker = User::factory()->create(['role' => 'apoteker']);

        $kategori = Kategori::create(['nama' => 'Antibiotik']);
        $satuan   = Satuan::create(['nama' => 'Tablet']);

        $this->obatMasuk = ObatMasuk::create([
            'nama_obat'          => 'Amoxicillin',
            'kategori_id'        => $kategori->id,
            'satuan_id'          => $satuan->id,
            'kode_batch'         => 'BTH-001',
            'stok'               => 100,
            'tanggal_penerimaan' => now()->toDateString(),
            'tanggal_kadaluarsa' => now()->addYear()->toDateString(),
            'user_id'            => $this->dokter->id,
        ]);
    }

    private function resepData(array $overrides = []): array
    {
        return array_merge([
            'nama_dokter'    => 'Dr. Siti',
            'tanggal_resep'  => now()->toDateString(),
            'jenis_penjamin' => 'umum',
            'jenis_layanan'  => 'BP',
            'nama_pasien'    => 'Budi Santoso',
            'jenis_kelamin'  => 'L',
            'umur_pasien'    => 30,
            'berat_badan'    => 65.5,
            'diagnosa'       => 'ISPA',
            'items'          => [
                [
                    'obat_masuk_id' => $this->obatMasuk->id,
                    'jumlah'        => 10,
                    'aturan_pakai'  => '3x sehari',
                ],
            ],
        ], $overrides);
    }

    private function createResep(array $attrs = []): Resep
    {
        return Resep::create(array_merge([
            'no_resep'       => 'RSP-TEST-' . rand(1000, 9999),
            'user_id'        => $this->dokter->id,
            'nama_dokter'    => 'Dr. Siti',
            'nama_pasien'    => 'Budi Santoso',
            'tanggal_resep'  => now()->toDateString(),
            'diagnosa'       => 'Flu',
            'status'         => 'proses',
            'jenis_penjamin' => 'umum',
            'jenis_layanan'  => 'BP',
            'jenis_kelamin'  => 'L',
            'umur_pasien'    => 30,
            'berat_badan'    => 60,
            'is_read'        => false,
        ], $attrs));
    }

    // ─────────────────────────────────────────
    // index
    // ─────────────────────────────────────────

    public function test_index_returns_200_for_authenticated_user(): void
    {
        $this->actingAs($this->dokter);
        $response = $this->get(route('resep.index'));
        $response->assertStatus(200);
    }

    public function test_index_redirects_guest(): void
    {
        $response = $this->get(route('resep.index'));
        $response->assertRedirect(route('login'));
    }

    public function test_index_search_by_nama_pasien(): void
    {
        $this->actingAs($this->dokter);
        $this->createResep(['nama_pasien' => 'Budi Santoso', 'no_resep' => 'RSP-001']);
        $this->createResep(['nama_pasien' => 'Siti Rahayu', 'no_resep' => 'RSP-002']);

        $response = $this->get(route('resep.index', ['search' => 'Budi']));
        $response->assertSee('Budi Santoso');
        $response->assertDontSee('Siti Rahayu');
    }

    public function test_index_filter_by_status(): void
    {
        $this->actingAs($this->dokter);
        $this->createResep(['no_resep' => 'RSP-001', 'status' => 'proses']);
        $this->createResep(['no_resep' => 'RSP-002', 'status' => 'selesai', 'nama_pasien' => 'Pasien Selesai']);

        $response = $this->get(route('resep.index', ['status' => 'proses']));
        $response->assertSee('Budi Santoso');
        $response->assertDontSee('Pasien Selesai');
    }

    // ─────────────────────────────────────────
    // store — berhasil
    // ─────────────────────────────────────────

    public function test_store_creates_resep_with_items_and_obat_keluar(): void
    {
        $this->actingAs($this->dokter);

        $response = $this->post(route('resep.store'), $this->resepData());

        $response->assertRedirect(route('resep.index'));
        $response->assertSessionHas('success');

        // Resep terbuat
        $this->assertDatabaseHas('reseps', ['nama_pasien' => 'Budi Santoso', 'status' => 'proses']);

        // ResepItem terbuat
        $this->assertDatabaseHas('resep_items', [
            'obat_masuk_id' => $this->obatMasuk->id,
            'jumlah'        => 10,
        ]);

        // ObatKeluar terbuat
        $this->assertDatabaseHas('obat_keluars', [
            'obat_masuk_id' => $this->obatMasuk->id,
            'jumlah'        => 10,
            'status'        => 'proses',
        ]);
    }

    public function test_store_does_not_decrement_stok_when_status_is_proses(): void
    {
        $this->actingAs($this->dokter);

        $this->post(route('resep.store'), $this->resepData([
            'items' => [
                ['obat_masuk_id' => $this->obatMasuk->id, 'jumlah' => 15, 'aturan_pakai' => '3x sehari'],
            ],
        ]));

        // Stok dari 100 tetap 100 karena statusnya 'proses'
        $this->assertEquals(100, $this->obatMasuk->fresh()->stok);
    }

    public function test_store_generates_unique_no_resep(): void
    {
        $this->actingAs($this->dokter);

        $this->post(route('resep.store'), $this->resepData());

        $resep = Resep::first();
        $this->assertStringStartsWith('RSP-', $resep->no_resep);
    }

    // ─────────────────────────────────────────
    // store — gagal karena stok
    // ─────────────────────────────────────────

    public function test_store_fails_when_item_stok_insufficient(): void
    {
        $this->actingAs($this->dokter);

        $response = $this->post(route('resep.store'), $this->resepData([
            'items' => [
                ['obat_masuk_id' => $this->obatMasuk->id, 'jumlah' => 200, 'aturan_pakai' => '1x sehari'],
            ],
        ]));

        $response->assertSessionHasErrors('items');
        $this->assertDatabaseCount('reseps', 0);
    }

    public function test_store_fails_without_required_fields(): void
    {
        $this->actingAs($this->dokter);

        $response = $this->post(route('resep.store'), []);
        $response->assertSessionHasErrors(['nama_dokter', 'tanggal_resep', 'nama_pasien', 'diagnosa', 'items']);
    }

    // ─────────────────────────────────────────
    // show
    // ─────────────────────────────────────────

    public function test_show_returns_200(): void
    {
        $this->actingAs($this->apoteker);
        $resep = $this->createResep();

        $response = $this->get(route('resep.show', $resep));
        $response->assertStatus(200);
    }

    public function test_show_marks_as_read_for_apoteker(): void
    {
        $this->actingAs($this->apoteker);
        $resep = $this->createResep(['is_read' => false]);

        $this->get(route('resep.show', $resep));

        $this->assertTrue($resep->fresh()->is_read);
    }

    public function test_show_does_not_mark_as_read_for_dokter(): void
    {
        $this->actingAs($this->dokter);
        $resep = $this->createResep(['is_read' => false]);

        $this->get(route('resep.show', $resep));

        $this->assertFalse($resep->fresh()->is_read);
    }

    // ─────────────────────────────────────────
    // edit — restrict selesai
    // ─────────────────────────────────────────

    public function test_edit_redirects_to_show_when_status_is_selesai(): void
    {
        $this->actingAs($this->dokter);
        $resep = $this->createResep(['status' => 'selesai']);

        $response = $this->get(route('resep.edit', $resep));
        $response->assertRedirect(route('resep.show', $resep));
        $response->assertSessionHas('error');
    }

    public function test_edit_returns_200_when_status_is_proses(): void
    {
        $this->actingAs($this->dokter);
        $resep = $this->createResep(['status' => 'proses']);

        $response = $this->get(route('resep.edit', $resep));
        $response->assertStatus(200);
    }

    // ─────────────────────────────────────────
    // update — restrict selesai
    // ─────────────────────────────────────────

    public function test_update_redirects_to_show_when_resep_is_selesai(): void
    {
        $this->actingAs($this->dokter);
        $resep = $this->createResep(['status' => 'selesai']);

        $response = $this->put(route('resep.update', $resep), $this->resepData(['status' => 'proses']));
        $response->assertRedirect(route('resep.show', $resep));
        $response->assertSessionHas('error');
    }

    // ─────────────────────────────────────────
    // update — berhasil
    // ─────────────────────────────────────────

    public function test_update_does_not_deduct_stok_when_status_is_proses(): void
    {
        $this->actingAs($this->dokter);

        // Buat resep awal (status proses, stok utuh 100)
        $resep = $this->createResep();
        ResepItem::create([
            'resep_id'      => $resep->id,
            'obat_masuk_id' => $this->obatMasuk->id,
            'nama_obat'     => 'Amoxicillin',
            'jumlah'        => 10,
        ]);

        // Update dengan item baru jumlah 20, status tetap proses
        $response = $this->put(route('resep.update', $resep), $this->resepData([
            'status' => 'proses',
            'items'  => [
                ['obat_masuk_id' => $this->obatMasuk->id, 'jumlah' => 20, 'aturan_pakai' => '1x sehari'],
            ],
        ]));

        $response->assertRedirect(route('resep.index'));
        // Stok tetap 100 karena masih proses
        $this->assertEquals(100, $this->obatMasuk->fresh()->stok);
    }
    
    public function test_update_deducts_stok_when_status_changed_to_selesai(): void
    {
        $this->actingAs($this->dokter);

        // Buat resep awal (status proses, stok utuh 100)
        $resep = $this->createResep();
        ResepItem::create([
            'resep_id'      => $resep->id,
            'obat_masuk_id' => $this->obatMasuk->id,
            'nama_obat'     => 'Amoxicillin',
            'jumlah'        => 10,
        ]);

        // Update status menjadi selesai dan jumlah menjadi 20
        $response = $this->put(route('resep.update', $resep), $this->resepData([
            'status' => 'selesai',
            'items'  => [
                ['obat_masuk_id' => $this->obatMasuk->id, 'jumlah' => 20, 'aturan_pakai' => '1x sehari'],
            ],
        ]));

        $response->assertRedirect(route('resep.index'));
        // Stok: berkurang 20 → 80
        $this->assertEquals(80, $this->obatMasuk->fresh()->stok);
    }

    // ─────────────────────────────────────────
    // updateStatus
    // ─────────────────────────────────────────

    public function test_update_status_changes_resep_and_obat_keluar_status(): void
    {
        $this->actingAs($this->apoteker);
        $resep = $this->createResep(['status' => 'proses']);

        // Buat item resep
        ResepItem::create([
            'resep_id'      => $resep->id,
            'obat_masuk_id' => $this->obatMasuk->id,
            'nama_obat'     => 'Amoxicillin',
            'jumlah'        => 10,
        ]);

        // Buat obat keluar terkait dengan no_pengeluaran = no_resep
        ObatKeluar::create([
            'obat_masuk_id'       => $this->obatMasuk->id,
            'nama_obat'           => 'Amoxicillin',
            'no_pengeluaran'      => $resep->no_resep,
            'jumlah'              => 10,
            'tujuan_pemakaian'    => 'Resep',
            'tanggal_pengeluaran' => now()->toDateString(),
            'tanggal_kadaluarsa'  => now()->addYear()->toDateString(),
            'nama_petugas'        => 'Petugas',
            'nama_penerima'       => 'Pasien',
            'kode_batch'          => 'BTH-001',
            'status'              => 'proses',
            'user_id'             => $this->apoteker->id,
        ]);

        $response = $this->patch(route('resep.update-status', $resep), [
            'status' => 'selesai',
        ]);

        $response->assertRedirect(route('resep.index'));
        $this->assertEquals('selesai', $resep->fresh()->status);

        // ObatKeluar terkait juga diupdate
        $this->assertDatabaseHas('obat_keluars', [
            'no_pengeluaran' => $resep->no_resep,
            'status'         => 'selesai',
        ]);
        
        // Stok harus berkurang karena status berubah menjadi selesai
        $this->assertEquals(90, $this->obatMasuk->fresh()->stok);
    }

    public function test_update_status_fails_when_resep_already_selesai(): void
    {
        $this->actingAs($this->apoteker);
        $resep = $this->createResep(['status' => 'selesai']);

        $response = $this->patch(route('resep.update-status', $resep), [
            'status' => 'dibatalkan',
        ]);

        $response->assertRedirect(route('resep.index'));
        $response->assertSessionHas('error');

        // Status tidak berubah
        $this->assertEquals('selesai', $resep->fresh()->status);
    }

    public function test_update_status_validates_status_value(): void
    {
        $this->actingAs($this->apoteker);
        $resep = $this->createResep(['status' => 'proses']);

        $response = $this->patch(route('resep.update-status', $resep), [
            'status' => 'invalid-status',
        ]);

        $response->assertSessionHasErrors('status');
    }

    // ─────────────────────────────────────────
    // destroy
    // ─────────────────────────────────────────

    public function test_destroy_deletes_resep_items_and_restores_stok_if_selesai(): void
    {
        $this->actingAs($this->dokter);
        // Buat resep dengan status selesai
        $resep = $this->createResep(['status' => 'selesai']);

        // Buat item resep dan kurangi stok (karena selesai)
        ResepItem::create([
            'resep_id'      => $resep->id,
            'obat_masuk_id' => $this->obatMasuk->id,
            'nama_obat'     => 'Amoxicillin',
            'jumlah'        => 20,
        ]);
        $this->obatMasuk->decrement('stok', 20);

        // Buat obat keluar terkait
        ObatKeluar::create([
            'obat_masuk_id'       => $this->obatMasuk->id,
            'nama_obat'           => 'Amoxicillin',
            'no_pengeluaran'      => $resep->no_resep,
            'jumlah'              => 20,
            'tujuan_pemakaian'    => 'Resep test',
            'tanggal_pengeluaran' => now()->toDateString(),
            'tanggal_kadaluarsa'  => now()->addYear()->toDateString(),
            'nama_petugas'        => 'Petugas',
            'nama_penerima'       => 'Pasien',
            'kode_batch'          => 'BTH-001',
            'status'              => 'selesai',
            'user_id'             => $this->dokter->id,
        ]);

        $response = $this->delete(route('resep.destroy', $resep));

        $response->assertRedirect(route('resep.index'));
        $response->assertSessionHas('success');

        // Resep item dan resep terhapus
        $this->assertDatabaseMissing('reseps', ['id' => $resep->id]);
        $this->assertDatabaseMissing('resep_items', ['resep_id' => $resep->id]);

        // ObatKeluar terkait terhapus
        $this->assertDatabaseMissing('obat_keluars', ['no_pengeluaran' => $resep->no_resep]);

        // Stok dikembalikan: 80 → 100 (karena statusnya selesai)
        $this->assertEquals(100, $this->obatMasuk->fresh()->stok);
    }
    
    public function test_destroy_does_not_restore_stok_if_proses(): void
    {
        $this->actingAs($this->dokter);
        // Buat resep dengan status proses
        $resep = $this->createResep(['status' => 'proses']);

        // Buat item resep (stok tidak dikurangi karena proses)
        ResepItem::create([
            'resep_id'      => $resep->id,
            'obat_masuk_id' => $this->obatMasuk->id,
            'nama_obat'     => 'Amoxicillin',
            'jumlah'        => 20,
        ]);

        // Buat obat keluar terkait
        ObatKeluar::create([
            'obat_masuk_id'       => $this->obatMasuk->id,
            'nama_obat'           => 'Amoxicillin',
            'no_pengeluaran'      => $resep->no_resep,
            'jumlah'              => 20,
            'tujuan_pemakaian'    => 'Resep test',
            'tanggal_pengeluaran' => now()->toDateString(),
            'tanggal_kadaluarsa'  => now()->addYear()->toDateString(),
            'nama_petugas'        => 'Petugas',
            'nama_penerima'       => 'Pasien',
            'kode_batch'          => 'BTH-001',
            'status'              => 'proses',
            'user_id'             => $this->dokter->id,
        ]);

        $response = $this->delete(route('resep.destroy', $resep));

        $response->assertRedirect(route('resep.index'));
        $response->assertSessionHas('success');

        // Stok tetap 100 karena belum pernah dikurangi (status proses)
        $this->assertEquals(100, $this->obatMasuk->fresh()->stok);
    }
}
