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

class DocNumber
{
    /** @var CI_Controller */
    protected $CI;

    public function __construct()
    {
        $this->CI =& get_instance();
        $this->CI->load->database();
    }

    /**
     * Preview the next document number WITHOUT incrementing.
     * Returns a token you must pass to commit() after successful transaction.
     *
     * @param string $type        // e.g. 'invoice','receipt','quotation'
     * @param array  $whereExtra  // extra where filters, e.g. ['company_id' => 123]
     * @param int|null $padWidth  // override pad width; if null, uses DB value or 2
     * @return array|false        // token with 'doc_no' or false on not found
     */
    public function preview(string $type, array $whereExtra = [], ?int $padWidth = null)
    {
        // Adjust these column/table names to match your schema
        $table = 'doc_prefix';
        // Expected columns: id, docType, docPrefixCD, docYearCD, docCurrNo, padWidth(optional)

        $this->CI->db->from($table)
            ->select('docPrintForm,docPrefixID,company_id,docTypeID,docPrefixCD,docYearCD,docCurrNo' . (is_null($padWidth) ? ', padWidth' : ''))
            ->where('docPrintForm', $type);

        foreach ($whereExtra as $k => $v) {
            $this->CI->db->where($k, $v);
        }

        // $row = $this->CI->db->limit(1)->get()->row();
        // if (!$row) return false;
        $rowObj = $this->CI->db->limit(1)->get();

        if (!$rowObj) {
            // Query failed: log and return false
            log_message('error', 'DocNumber: DB query failed → ' . $this->CI->db->last_query());
            $status = [
                    'data'       => [],
                    'msg'        => "Document Prefix not set. Please update it.".$this->CI->db->last_query(),
                    'statusCode' => 2,
                    'flag'       => 'F',
                ];
            return $this->CI->response->output($status, 200);
        }
        $row = $rowObj->row();
        if (!$row) {
            $status = [
                    'data'       => [],
                    'msg'        => "Document Prefix not set. Please update it.".$this->CI->db->last_query(),
                    'statusCode' => 2,
                    'flag'       => 'F',
                ];
                return $this->CI->response->output($status, 200);
            //return false; // No record found
        }

        $pad = $padWidth ?? (isset($row->padWidth) && (int)$row->padWidth > 0 ? (int)$row->padWidth : 2);

        $doc_no = $this->formatNumber($row->docPrefixCD, (int)$row->docCurrNo, $row->docYearCD, $pad);
        if (!$doc_no) {
                $status = [
                    'data'       => [],
                    'msg'        => "Document Prefix not set. Please update it.",
                    'statusCode' => 2,
                    'flag'       => 'F',
                ];
                return $this->CI->response->output($status, 200);
            }
        // Return a token to be used for commit() to safely increment later
        return [
            'docPrefixID'        => (int)$row->docPrefixID,
            'type'      => $row->docPrintForm,
            'prefix'    => $row->docPrefixCD,
            'year'      => $row->docYearCD,
            'curr_no'   => (int)$row->docCurrNo,  // used for optimistic lock
            'pad'       => $pad,
            'where'     => $whereExtra,
            'doc_no'    => $doc_no,
        ];
    }

    /**
     * Commit the next number (increment) IF the previously previewed value is still current.
     * Returns true on success; false if conflict (another process already incremented).
     *
     * @param array $token  // token returned from preview()
     * @return bool
     */
    public function commit(array $token)
    {
        $table = 'doc_prefix';

        $this->CI->db->trans_start();

        $this->CI->db->set('docCurrNo', 'docCurrNo + 1', false)
            ->where('docPrefixID', $token['docPrefixID'])
            // Optimistic lock: only increment if current number still equals what we previewed
            ->where('docCurrNo', $token['curr_no'])
             ->where('docPrintForm', $token['type']);

        // Re-apply extra scoping (company_id, branch_id, etc.) if provided
        if (!empty($token['where']) && is_array($token['where'])) {
            foreach ($token['where'] as $k => $v) {
                $this->CI->db->where($k, $v);
            }
        }

        $this->CI->db->update($table);

        $affected = $this->CI->db->affected_rows();
        $this->CI->db->trans_complete();
        $ee = ($this->CI->db->trans_status() !== false) && ($affected === 1);
        if(!$ee){
            $status = [
            'msg'        => 'Number conflict: please retry generating the document number.',
            'statusCode' => 409,
            'flag'       => 'F',
            ];
            return $this->CI->response->output($status, 200);
        }else{
            return $ee;
        }


    }

    /**
     * Helper to build the formatted number like "INV01/2025".
     */
    public function formatNumber(string $prefix, int $n, string $year, int $pad = 2): string
    {
        return $prefix . $year . '/'.sprintf("%0{$pad}d", $n);
    }
}