<?php
defined('BASEPATH') or exit('No direct script access allowed');

class Farmasi_model extends CI_Model
{
    public function list_resep(array $filters = [])
    {
        $this->db->select('r.resep_id, r.tanggal_resep, r.status_resep, r.created_at,
            p.nama_lengkap, p.rme_no, poli.nama_poli,
            dokter.nama_lengkap AS nama_dokter,
            reg.registrasi_id,
            COUNT(rd.resep_detail_id) AS total_item,
            COALESCE(SUM(rd.jumlah_resep), 0) AS total_qty_resep,
            COALESCE(SUM(rd.jumlah_serah), 0) AS total_qty_serah');
        $this->db->from('tb_resep r');
        $this->db->join('tb_registrasi reg', 'BINARY reg.registrasi_id = BINARY r.registrasi_id', '', false);
        $this->db->join('tb_pasien p', 'BINARY p.pasien_id = BINARY r.pasien_id', '', false);
        $this->db->join('tb_poli poli', 'BINARY poli.poli_id = BINARY r.id_poli', 'left', false);
        $this->db->join('tb_dokter d', 'BINARY d.dokter_id = BINARY r.dokter_id', 'left', false);
        $this->db->join('tb_petugas dokter', 'BINARY dokter.petugas_id = BINARY d.id_petugas', 'left', false);
        $this->db->join('tb_resep_detail rd', 'BINARY rd.resep_id = BINARY r.resep_id', 'left', false);

        if (!empty($filters['tanggal'])) {
            $this->db->where('DATE(r.tanggal_resep)', $filters['tanggal']);
        }
        if (!empty($filters['status']) && $filters['status'] !== 'all') {
            $this->db->where('r.status_resep', $filters['status']);
        }
        if (!empty($filters['keyword'])) {
            $keyword = trim($filters['keyword']);
            $this->db->group_start();
            $this->db->like('p.nama_lengkap', $keyword);
            $this->db->or_like('p.rme_no', $keyword);
            $this->db->or_like('dokter.nama_lengkap', $keyword);
            $this->db->group_end();
        }

        $this->db->group_by([
            'r.resep_id', 'r.tanggal_resep', 'r.status_resep', 'r.created_at',
            'p.nama_lengkap', 'p.rme_no', 'poli.nama_poli', 'dokter.nama_lengkap',
            'reg.registrasi_id'
        ]);
        $this->db->order_by('r.created_at', 'DESC');

        return $this->db->get()->result();
    }

    public function get_resep($resep_id)
    {
        $header = $this->db
            ->select('r.*, p.nama_lengkap, p.rme_no, p.tanggal_lahir, p.jenis_kelamin,
                poli.nama_poli, dokter.nama_lengkap AS nama_dokter,
                petugas.nama_lengkap AS nama_petugas_farmasi')
            ->from('tb_resep r')
            ->join('tb_pasien p', 'BINARY p.pasien_id = BINARY r.pasien_id', '', false)
            ->join('tb_poli poli', 'BINARY poli.poli_id = BINARY r.id_poli', 'left', false)
            ->join('tb_dokter d', 'BINARY d.dokter_id = BINARY r.dokter_id', 'left', false)
            ->join('tb_petugas dokter', 'BINARY dokter.petugas_id = BINARY d.id_petugas', 'left', false)
            ->join('tb_petugas petugas', 'BINARY petugas.petugas_id = BINARY r.processed_by', 'left', false)
            ->where('r.resep_id', $resep_id)
            ->get()
            ->row();

        if (!$header) {
            return null;
        }

        $detail = $this->db
            ->select('rd.*, o.kode_obat, o.nama_obat AS nama_master_obat,
                o.satuan_kecil, s.qty AS stok_farmasi')
            ->from('tb_resep_detail rd')
            ->join('tb_master_obat o', 'BINARY o.obat_id = BINARY rd.obat_id', 'left', false)
            ->join('tb_stok_obat s', "BINARY s.obat_id = BINARY rd.obat_id AND BINARY s.lokasi_id = '" . $this->db->escape_str($this->get_farmasi_lokasi_id()) . "'", 'left', false)
            ->where('rd.resep_id', $resep_id)
            ->order_by('rd.created_at', 'ASC')
            ->get()
            ->result();

        return [
            'header' => $header,
            'detail' => $detail,
        ];
    }

    public function list_master_obat($keyword = '')
    {
        $this->db->select('o.*, bs.nama_bentuk AS bentuk_sediaan');
        $this->db->from('tb_master_obat o');
        $this->db->join('tb_bentuk_sediaan bs', 'BINARY bs.bentuk_sediaan_id = BINARY o.bentuk_sediaan_id', 'left', false);

        if ($keyword !== '') {
            $this->db->group_start();
            $this->db->like('o.kode_obat', $keyword);
            $this->db->or_like('o.nama_obat', $keyword);
            $this->db->group_end();
        }

        return $this->db
            ->order_by('o.nama_obat', 'ASC')
            ->get()
            ->result();
    }

    public function list_bentuk_sediaan()
    {
        return $this->db
            ->where('status_aktif', 1)
            ->order_by('nama_bentuk', 'ASC')
            ->get('tb_bentuk_sediaan')
            ->result();
    }

    public function get_active_obat_options()
    {
        return $this->db
            ->select('obat_id, kode_obat, nama_obat, satuan_kecil, stok_minimum')
            ->where('status_aktif', 1)
            ->order_by('nama_obat', 'ASC')
            ->get('tb_master_obat')
            ->result();
    }

    public function save_master_obat(array $data)
    {
        $existing = $this->db->get_where('tb_master_obat', ['kode_obat' => $data['kode_obat']])->row();
        if ($existing) {
            return [false, 'Kode obat sudah digunakan.'];
        }

        $payload = [
            'obat_id'         => generate_uuid(),
            'kode_obat'       => $data['kode_obat'],
            'nama_obat'       => $data['nama_obat'],
            'bentuk_sediaan_id'=> $data['bentuk_sediaan_id'] ?: null,
            'satuan_kecil'    => $data['satuan_kecil'],
            'kategori'        => $data['kategori'],
            'harga_beli'      => $data['harga_beli'],
            'harga_jual'      => $data['harga_jual'],
            'stok_minimum'    => $data['stok_minimum'],
            'status_aktif'    => 1,
            'created_at'      => date('Y-m-d H:i:s'),
            'updated_at'      => date('Y-m-d H:i:s'),
        ];

        $this->db->insert('tb_master_obat', $payload);

        return [true, 'Master obat berhasil ditambahkan.'];
    }

    public function save_bentuk_sediaan(array $data)
    {
        $existing = $this->db->get_where('tb_bentuk_sediaan', ['nama_bentuk' => $data['nama_bentuk']])->row();
        if ($existing) {
            return [false, 'Bentuk sediaan sudah ada.'];
        }

        $this->db->insert('tb_bentuk_sediaan', [
            'bentuk_sediaan_id' => generate_uuid(),
            'nama_bentuk' => $data['nama_bentuk'],
            'deskripsi' => $data['deskripsi'],
            'status_aktif' => 1,
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s'),
        ]);

        return [true, 'Bentuk sediaan berhasil ditambahkan.'];
    }

    public function stok_ringkas()
    {
        $this->db->select('s.stok_id, s.qty, s.updated_at,
            o.obat_id, o.kode_obat, o.nama_obat, o.satuan_kecil, o.stok_minimum,
            l.lokasi_id, l.nama_lokasi, l.jenis_lokasi');
        $this->db->from('tb_stok_obat s');
        $this->db->join('tb_master_obat o', 'BINARY o.obat_id = BINARY s.obat_id', '', false);
        $this->db->join('tb_lokasi_stok l', 'BINARY l.lokasi_id = BINARY s.lokasi_id', '', false);
        $this->db->order_by('l.nama_lokasi', 'ASC');
        $this->db->order_by('o.nama_obat', 'ASC');

        return $this->db->get()->result();
    }

    public function list_lokasi()
    {
        return $this->db
            ->where('status_aktif', 1)
            ->order_by('nama_lokasi', 'ASC')
            ->get('tb_lokasi_stok')
            ->result();
    }

    public function list_mutasi(array $filters = [])
    {
        $this->db->select('m.*, o.kode_obat, o.nama_obat, o.satuan_kecil, l.nama_lokasi,
            p.nama_lengkap AS nama_petugas');
        $this->db->from('tb_mutasi_stok m');
        $this->db->join('tb_master_obat o', 'BINARY o.obat_id = BINARY m.obat_id', '', false);
        $this->db->join('tb_lokasi_stok l', 'BINARY l.lokasi_id = BINARY m.lokasi_id', '', false);
        $this->db->join('tb_petugas p', 'BINARY p.petugas_id = BINARY m.created_by', 'left', false);

        if (!empty($filters['tanggal'])) {
            $this->db->where('DATE(m.created_at)', $filters['tanggal']);
        }
        if (!empty($filters['lokasi_id']) && $filters['lokasi_id'] !== 'all') {
            $this->db->where('m.lokasi_id', $filters['lokasi_id']);
        }
        if (!empty($filters['jenis_mutasi']) && $filters['jenis_mutasi'] !== 'all') {
            $this->db->where('m.jenis_mutasi', $filters['jenis_mutasi']);
        }

        $this->db->order_by('m.created_at', 'DESC');

        return $this->db->get()->result();
    }

    public function tambah_stok(array $data)
    {
        $this->db->trans_begin();

        $stok = $this->get_stok_row($data['obat_id'], $data['lokasi_id'], true);
        $saldoSebelum = $stok ? (float) $stok->qty : 0.0;
        $saldoSesudah = $saldoSebelum + (float) $data['qty'];

        $this->upsert_stok($data['obat_id'], $data['lokasi_id'], $saldoSesudah);

        $this->insert_mutasi([
            'obat_id'        => $data['obat_id'],
            'lokasi_id'      => $data['lokasi_id'],
            'resep_id'       => null,
            'referensi_tipe' => 'PEMBELIAN',
            'referensi_id'   => null,
            'jenis_mutasi'   => 'MASUK',
            'qty'            => $data['qty'],
            'saldo_sebelum'  => $saldoSebelum,
            'saldo_sesudah'  => $saldoSesudah,
            'keterangan'     => $data['keterangan'],
            'created_by'     => $data['created_by'],
        ]);

        if ($this->db->trans_status() === false) {
            $this->db->trans_rollback();
            return [false, 'Gagal menambah stok.'];
        }

        $this->db->trans_commit();
        return [true, 'Stok berhasil ditambahkan.'];
    }

    public function penyesuaian_stok(array $data)
    {
        $this->db->trans_begin();

        $stok = $this->get_stok_row($data['obat_id'], $data['lokasi_id'], true);
        $saldoSebelum = $stok ? (float) $stok->qty : 0.0;
        $saldoSesudah = (float) $data['qty_baru'];
        $selisih = $saldoSesudah - $saldoSebelum;

        $this->upsert_stok($data['obat_id'], $data['lokasi_id'], $saldoSesudah);

        $this->insert_mutasi([
            'obat_id'        => $data['obat_id'],
            'lokasi_id'      => $data['lokasi_id'],
            'resep_id'       => null,
            'referensi_tipe' => 'PENYESUAIAN',
            'referensi_id'   => null,
            'jenis_mutasi'   => 'ADJUSTMENT',
            'qty'            => $selisih,
            'saldo_sebelum'  => $saldoSebelum,
            'saldo_sesudah'  => $saldoSesudah,
            'keterangan'     => $data['keterangan'],
            'created_by'     => $data['created_by'],
        ]);

        if ($this->db->trans_status() === false) {
            $this->db->trans_rollback();
            return [false, 'Gagal menyesuaikan stok.'];
        }

        $this->db->trans_commit();
        return [true, 'Stok berhasil disesuaikan.'];
    }

    public function dispense_resep($resep_id, array $serahRows, $petugasId)
    {
        $resep = $this->get_resep($resep_id);
        if (!$resep) {
            return [false, 'Resep tidak ditemukan.'];
        }
        if ($resep['header']->status_resep === 'SELESAI') {
            return [false, 'Resep sudah diserahkan.'];
        }

        $farmasiLokasiId = $this->get_farmasi_lokasi_id();
        if (!$farmasiLokasiId) {
            return [false, 'Lokasi farmasi belum tersedia.'];
        }

        $mapRows = [];
        foreach ($serahRows as $row) {
            $mapRows[$row['resep_detail_id']] = $row;
        }

        $this->db->trans_begin();

        $semuaSelesai = true;
        foreach ($resep['detail'] as $detail) {
            if (!isset($mapRows[$detail->resep_detail_id])) {
                continue;
            }

            $qtySerahBaru = (float) $mapRows[$detail->resep_detail_id]['jumlah_serah'];
            $qtySudahSerah = (float) $detail->jumlah_serah;
            $tambahanSerah = $qtySerahBaru - $qtySudahSerah;
            $qtyResep = (float) $detail->jumlah_resep;

            if ($qtySerahBaru < $qtySudahSerah || $qtySerahBaru > $qtyResep) {
                $this->db->trans_rollback();
                return [false, 'Jumlah serah tidak valid untuk item ' . $detail->nama_obat_snapshot . '.'];
            }

            if ($tambahanSerah > 0) {
                $stok = $this->get_stok_row($detail->obat_id, $farmasiLokasiId, true);
                $saldoSebelum = $stok ? (float) $stok->qty : 0.0;
                if ($saldoSebelum < $tambahanSerah) {
                    $this->db->trans_rollback();
                    return [false, 'Stok obat ' . $detail->nama_obat_snapshot . ' tidak mencukupi.'];
                }

                $saldoSesudah = $saldoSebelum - $tambahanSerah;
                $this->upsert_stok($detail->obat_id, $farmasiLokasiId, $saldoSesudah);
                $this->insert_mutasi([
                    'obat_id'        => $detail->obat_id,
                    'lokasi_id'      => $farmasiLokasiId,
                    'resep_id'       => $resep_id,
                    'referensi_tipe' => 'RESEP',
                    'referensi_id'   => $detail->resep_detail_id,
                    'jenis_mutasi'   => 'KELUAR',
                    'qty'            => $tambahanSerah,
                    'saldo_sebelum'  => $saldoSebelum,
                    'saldo_sesudah'  => $saldoSesudah,
                    'keterangan'     => 'Penyerahan resep pasien ' . $resep['header']->nama_lengkap,
                    'created_by'     => $petugasId,
                ]);
            }

            $statusDetail = 'MENUNGGU';
            if ($qtySerahBaru >= $qtyResep && $qtyResep > 0) {
                $statusDetail = 'SELESAI';
            } elseif ($qtySerahBaru > 0) {
                $statusDetail = 'SEBAGIAN';
                $semuaSelesai = false;
            } else {
                $semuaSelesai = false;
            }

            $this->db
                ->where('resep_detail_id', $detail->resep_detail_id)
                ->update('tb_resep_detail', [
                    'jumlah_serah' => $qtySerahBaru,
                    'status_detail' => $statusDetail,
                ]);
        }

        $statusResep = $semuaSelesai ? 'SELESAI' : 'DIPROSES';
        $this->db
            ->where('resep_id', $resep_id)
            ->update('tb_resep', [
                'status_resep' => $statusResep,
                'processed_by' => $petugasId,
                'processed_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ]);

        if ($this->db->trans_status() === false) {
            $this->db->trans_rollback();
            return [false, 'Gagal memproses penyerahan resep.'];
        }

        $this->db->trans_commit();
        return [true, $statusResep === 'SELESAI' ? 'Resep selesai diserahkan.' : 'Resep diproses sebagian.'];
    }

    public function sync_resep_dari_pelayanan($pelayanan_id, $registrasi_id, array $payload)
    {
        $rows = array_values(array_filter($payload['terapi'], function ($row) {
            return !empty($row['obat_id']) || !empty($row['nama_obat']);
        }));

        $existing = $this->db->get_where('tb_resep', ['pelayanan_id' => $pelayanan_id])->row();

        if (empty($rows)) {
            if ($existing && $existing->status_resep !== 'SELESAI') {
                $this->db->where('resep_id', $existing->resep_id)->delete('tb_resep_detail');
                $this->db->where('resep_id', $existing->resep_id)->delete('tb_resep');
            }
            return true;
        }

        if ($existing && $existing->status_resep === 'SELESAI') {
            return true;
        }

        $resepId = $existing ? $existing->resep_id : generate_uuid();
        $header = [
            'resep_id'      => $resepId,
            'pelayanan_id'  => $pelayanan_id,
            'registrasi_id' => $registrasi_id,
            'pasien_id'     => $payload['id_pasien'],
            'dokter_id'     => $payload['id_dokter'],
            'id_poli'       => $payload['id_poli'],
            'tanggal_resep' => date('Y-m-d H:i:s'),
            'status_resep'  => 'MENUNGGU',
            'catatan'       => null,
            'updated_at'    => date('Y-m-d H:i:s'),
        ];

        if ($existing) {
            $this->db->where('resep_id', $resepId)->update('tb_resep', $header);
            $this->db->where('resep_id', $resepId)->delete('tb_resep_detail');
        } else {
            $header['created_at'] = date('Y-m-d H:i:s');
            $this->db->insert('tb_resep', $header);
        }

        foreach ($rows as $row) {
            $this->db->insert('tb_resep_detail', [
                'resep_detail_id'     => generate_uuid(),
                'resep_id'            => $resepId,
                'obat_id'             => $row['obat_id'] ?: null,
                'nama_obat_snapshot'  => $row['nama_obat'],
                'dosis'               => $row['dosis'],
                'frekuensi'           => $row['frekuensi'],
                'rute'                => $row['rute'],
                'durasi'              => $row['durasi'],
                'jumlah_resep'        => (float) ($row['jumlah'] ?: 0),
                'jumlah_serah'        => 0,
                'satuan_snapshot'     => $row['satuan'],
                'catatan'             => $row['catatan'],
                'status_detail'       => 'MENUNGGU',
                'created_at'          => date('Y-m-d H:i:s'),
            ]);
        }

        return true;
    }

    public function get_farmasi_lokasi_id()
    {
        $row = $this->db
            ->where('jenis_lokasi', 'FARMASI')
            ->where('status_aktif', 1)
            ->order_by('created_at', 'ASC')
            ->get('tb_lokasi_stok')
            ->row();

        return $row ? $row->lokasi_id : null;
    }

    private function get_stok_row($obatId, $lokasiId, $forUpdate = false)
    {
        $sql = 'SELECT * FROM tb_stok_obat WHERE obat_id = ? AND lokasi_id = ?';
        if ($forUpdate) {
            $sql .= ' FOR UPDATE';
        }

        return $this->db->query($sql, [$obatId, $lokasiId])->row();
    }

    private function upsert_stok($obatId, $lokasiId, $qty)
    {
        $existing = $this->db->get_where('tb_stok_obat', [
            'obat_id' => $obatId,
            'lokasi_id' => $lokasiId,
        ])->row();

        if ($existing) {
            $this->db
                ->where('stok_id', $existing->stok_id)
                ->update('tb_stok_obat', [
                    'qty' => $qty,
                    'updated_at' => date('Y-m-d H:i:s'),
                ]);
            return;
        }

        $this->db->insert('tb_stok_obat', [
            'stok_id' => generate_uuid(),
            'obat_id' => $obatId,
            'lokasi_id' => $lokasiId,
            'qty' => $qty,
            'updated_at' => date('Y-m-d H:i:s'),
        ]);
    }

    private function insert_mutasi(array $data)
    {
        $this->db->insert('tb_mutasi_stok', [
            'mutasi_id'      => generate_uuid(),
            'obat_id'        => $data['obat_id'],
            'lokasi_id'      => $data['lokasi_id'],
            'resep_id'       => $data['resep_id'],
            'referensi_tipe' => $data['referensi_tipe'],
            'referensi_id'   => $data['referensi_id'],
            'jenis_mutasi'   => $data['jenis_mutasi'],
            'qty'            => $data['qty'],
            'saldo_sebelum'  => $data['saldo_sebelum'],
            'saldo_sesudah'  => $data['saldo_sesudah'],
            'keterangan'     => $data['keterangan'],
            'created_by'     => $data['created_by'],
            'created_at'     => date('Y-m-d H:i:s'),
        ]);
    }
}
