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

/**
 * Permission Model
 * 
 * Handles database queries for role-based access control.
 * 
 * @version 1.0
 * @date 2026-01-15
 */
class Permission_model extends CI_Model {

    public function __construct() {
        parent::__construct();
    }

    /**
     * Check if a role has a specific permission
     * 
     * @param int $role_id The role ID
     * @param string $permission_key The permission key
     * @return bool True if allowed
     */
    public function has_permission($role_id, $permission_key) {
        $result = $this->db->select('is_allowed')
            ->where('role_id', $role_id)
            ->where('permission_key', $permission_key)
            ->get('role_permissions')
            ->row();
        
        return $result && $result->is_allowed;
    }

    /**
     * Get all permissions for a role
     * 
     * @param int $role_id The role ID
     * @return array Array of permission keys that are allowed
     */
    public function get_role_permissions($role_id) {
        $results = $this->db->select('permission_key')
            ->where('role_id', $role_id)
            ->where('is_allowed', true)
            ->get('role_permissions')
            ->result();
        
        return array_column($results, 'permission_key');
    }

    /**
     * Check if user has access to a company
     * 
     * @param int $user_id The user ID
     * @param int $company_id The company ID
     * @return bool True if user has access
     */
    public function user_has_company_access($user_id, $company_id) {
        $count = $this->db->where('user_id', $user_id)
            ->where('company_id', $company_id)
            ->count_all_results('user_companies');
        
        return $count > 0;
    }

    /**
     * Get all company IDs (for Super Admin)
     * 
     * @return array Array of company IDs
     */
    public function get_all_company_ids() {
        $results = $this->db->select('id')
            ->get('company')
            ->result();
        
        return array_column($results, 'id');
    }

    /**
     * Get company IDs assigned to a user
     * 
     * @param int $user_id The user ID
     * @return array Array of company IDs
     */
    public function get_user_company_ids($user_id) {
        $results = $this->db->select('company_id')
            ->where('user_id', $user_id)
            ->get('user_companies')
            ->result();
        
        return array_column($results, 'company_id');
    }

    /**
     * Get all authority IDs
     * 
     * @return array Array of authority IDs
     */
    public function get_all_authority_ids() {
        $results = $this->db->select('id')
            ->where('status', '1')
            ->get('authority')
            ->result();
        
        return array_column($results, 'id');
    }

    /**
     * Get enabled authorities for a company
     * 
     * @param int $company_id The company ID
     * @return array Array of authority IDs
     */
    public function get_enabled_authorities_for_company($company_id) {
        // Updated to use authority.company_id directly (no more company_authorities bridge table)
        $results = $this->db->select('id as authority_id')
            ->where('company_id', $company_id)
            ->where('status', '1')
            ->where('is_delete', '0')
            ->get('authority')
            ->result();
        
        return array_column($results, 'authority_id');
    }

    /**
     * Get authority IDs assigned to a user
     * 
     * @param int $user_id The user ID
     * @return array Array of authority IDs
     */
    public function get_user_authority_ids($user_id) {
        $results = $this->db->select('authority_id')
            ->where('user_id', $user_id)
            ->get('user_authorities')
            ->result();
        
        return array_column($results, 'authority_id');
    }

    /**
     * Get all document type IDs
     * 
     * @return array Array of type IDs
     */
    public function get_all_document_type_ids() {
        $results = $this->db->select('id')
            ->where('status', '1')
            ->get('sub_type')
            ->result();
        
        return array_column($results, 'id');
    }

    /**
     * Get enabled document types for a company
     * 
     * @param int $company_id The company ID
     * @return array Array of type IDs
     */
    public function get_enabled_document_types_for_company($company_id) {
        // Updated to use company_id directly in sub_type (no more company_document_heads bridge table)
        $results = $this->db->select('id as type_id')
            ->where('company_id', $company_id)
            ->where('is_enabled', true)
            ->where('status', '1')
            ->get('sub_type')
            ->result();
        
        return array_column($results, 'type_id');
    }

    /**
     * Get document type IDs assigned to a user
     * 
     * @param int $user_id The user ID
     * @return array Array of type IDs
     */
    public function get_user_document_type_ids($user_id) {
        $results = $this->db->select('type_id')
            ->where('user_id', $user_id)
            ->get('user_document_types')
            ->result();
        
        return array_column($results, 'type_id');
    }

    /**
     * Get companies accessible by a user with full details
     * 
     * @param int $user_id The user ID
     * @param int $role_id The user's role ID
     * @return array Array of company objects
     */
    public function get_accessible_companies($user_id, $role_id) {
        if ($role_id == '1') {
            // Super Admin gets all companies
            return $this->db->get('company')->result();
        }
        
        // Other roles get assigned companies
        return $this->db->select('c.*')
            ->from('company c')
            ->join('user_companies uc', 'c.id = uc.company_id')
            ->where('uc.user_id', $user_id)
            ->get()
            ->result();
    }

    /**
     * Get enabled document heads for a company
     * 
     * @param int $company_id The company ID
     * @param int|null $authority_id Optional authority filter
     * @return array Array of document head objects with company settings
     */
    public function get_company_document_heads($company_id, $authority_id = null) {
        // Updated to use company_id directly in sub_type (no more company_document_heads bridge table)
        $this->db->select("st.*, st.document_start_date as effective_start_date,
            st.frequency_start_date as effective_frequency_start_date,
            st.due_in_same_next_month as effective_due_in_same_next_month",false)
            ->from('sub_type st')
            ->where('st.company_id', (int)$company_id)
            ->where('st.status', '1')
            ->where('st.is_enabled', true);
        
        if ($authority_id) {
            $this->db->where('st.authority_id', $authority_id);
        }
        
        return $this->db->get()->result();
    }

    /**
     * Get enabled documents for a company and document type
     * 
     * @param int $company_id The company ID
     * @param int $type_id The document type ID
     * @return array Array of document objects with company settings
     */
    public function get_company_documents($company_id, $type_id) {
        // Updated to use mandatory_documents with company_id directly
        return $this->db->select('d.*, md.mandatory as is_mandatory')
            ->from('documents d')
            ->join('mandatory_documents md', 
                'd.id = md.document_id AND md.company_id = ' . (int)$company_id . ' AND md.type_id = ' . (int)$type_id, 
                'inner')
            ->where('d.status', '1')
            ->get()
            ->result();
    }

    /**
     * Get authorities with enabled document heads for a company
     * 
     * @param int $company_id The company ID
     * @return array Array of authority objects
     */
    public function get_company_authorities($company_id) {
        // Updated to use authority.company_id directly (no more company_authorities bridge table)
        return $this->db->select('a.*')
            ->from('authority a')
            ->where('a.company_id', $company_id)
            ->where('a.status', '1')
            ->where('a.is_delete', '0')
            ->get()
            ->result();
    }

    /**
     * Activate a document head for a company
     * Cascades to enable parent authority and child documents
     * 
     * @param int $company_id The company ID
     * @param int $type_id The document type ID
     * @param string $custom_start_date The company-specific start date
     * @param int $enabled_by User ID who enabled it
     * @return array Success/error response
     */
    public function activate_document_head($company_id, $type_id, $custom_start_date, $enabled_by) {
        // Validate start date against company start date
        $company = $this->db->get_where('company', ['id' => $company_id])->row();
        if (!$company) {
            return ['success' => false, 'message' => 'Company not found'];
        }
        
        if ($custom_start_date && strtotime($custom_start_date) < strtotime($company->co_start_date)) {
            return [
                'success' => false, 
                'message' => 'Document start date cannot be earlier than company start date (' . date('d-m-Y', strtotime($company->co_start_date)) . ')'
            ];
        }
        
        // Get document head info
        $doc_head = $this->db->get_where('sub_type', ['id' => $type_id])->row();
        if (!$doc_head) {
            return ['success' => false, 'message' => 'Document head not found'];
        }
        
        $this->db->trans_start();
        
        // Updated to use is_enabled directly on sub_type (no more company_document_heads bridge table)
        // 1. Enable the document head in sub_type
        $this->db->where('id', $type_id)
            ->where('company_id', $company_id)
            ->update('sub_type', [
                'is_enabled' => true,
                'document_start_date' => $custom_start_date ?: null
            ]);
        
        // 2. Enable authority (now directly on authority table with company_id)
        $authority_id = $doc_head->authority_id;
        $this->db->where('id', $authority_id)
            ->where('company_id', $company_id)
            ->update('authority', ['status' => '1']);
        
        // 3. Enable mandatory documents (now directly on mandatory_documents with is_enabled)
        $this->db->where(['company_id' => $company_id, 'type_id' => $type_id])
            ->update('mandatory_documents', ['is_enabled' => true]);
        
        // 4. Enable reminders (now directly on sub_type_reminders)
        $this->db->where(['company_id' => $company_id, 'type_id' => $type_id])
            ->update('sub_type_reminders', ['is_enabled' => true]);
        
        $this->db->trans_complete();
        
        if ($this->db->trans_status() === FALSE) {
            return ['success' => false, 'message' => 'Database error occurred'];
        }
        
        return ['success' => true, 'message' => 'Document head activated successfully'];
    }

    /**
     * Deactivate a document head for a company
     * 
     * @param int $company_id The company ID
     * @param int $type_id The document type ID
     * @return array Success/error response
     */
    public function deactivate_document_head($company_id, $type_id, $deactivation_date = null, $deactivation_remarks = null) {
        // Get document head info
        $doc_head = $this->db->get_where('sub_type', ['id' => $type_id])->row();
        if (!$doc_head) {
            return ['success' => false, 'message' => 'Document head not found'];
        }
        
        $authority_id = $doc_head->authority_id;
        
        $this->db->trans_start();
        
        // Updated to use is_enabled directly on sub_type (no more company_document_heads bridge table)
        // 1. Disable the document head in sub_type with deactivation details
        $update_data = [
            'is_enabled' => false
        ];
        
        // Save deactivation details if provided
        if (!empty($deactivation_date)) {
            $update_data['deactivation_date'] = $deactivation_date;
            $update_data['deactivation_remarks'] = $deactivation_remarks;
        }
        
        $this->db->where('id', $type_id)
            ->where('company_id', $company_id)
            ->update('sub_type', $update_data);
        
        // 2. Disable related mandatory documents
        $this->db->where(['company_id' => $company_id, 'type_id' => $type_id])
            ->update('mandatory_documents', ['is_enabled' => false]);
        
        // 3. Disable reminders
        $this->db->where(['company_id' => $company_id, 'type_id' => $type_id])
            ->update('sub_type_reminders', ['is_enabled' => false]);
        
        // 4. Check if authority has other enabled doc heads for this company
        $other_enabled = $this->db->where('company_id', $company_id)
            ->where('is_enabled', true)
            ->where('authority_id', $authority_id)
            ->where('id !=', $type_id)
            ->count_all_results('sub_type');
        
        // 5. If no other doc heads use this authority, disable authority (optional)
        // Note: Authority now has company_id directly, so this just sets status
        if ($other_enabled == 0) {
            $this->db->where('id', $authority_id)
                ->where('company_id', $company_id)
                ->update('authority', ['status' => '0']);
        }
        
        $this->db->trans_complete();
        
        if ($this->db->trans_status() === FALSE) {
            return ['success' => false, 'message' => 'Database error occurred'];
        }
        
        return ['success' => true, 'message' => 'Document head deactivated successfully'];
    }

    /**
     * Get all available templates
     * 
     * @return array Array of template objects
     */
    public function get_templates() {
        return $this->db->where('is_active', true)
            ->get('company_templates')
            ->result();
    }

    /**
     * Copy template to company (all items disabled)
     * 
     * @param int $company_id The company ID
     * @param int $template_id The template ID
     * @param int $created_by User ID creating this
     * @return bool Success status
     */
    public function apply_template_to_company($company_id, $template_id, $created_by) {
        $this->db->trans_start();
        
        // Updated for company-wise masters - copy data directly to master tables with company_id
        
        // 1. Copy authorities from template to authority table for this company
        $this->db->query("
            INSERT INTO authority (authority_name, alias_name, company_id, status, is_delete, created_date)
            SELECT DISTINCT a.authority_name, a.alias_name, ?, '1', 0, NOW()
            FROM company_template_document_heads ctdh
            JOIN sub_type st ON st.id = ctdh.type_id
            JOIN authority a ON a.id::text = st.authority_id
            WHERE ctdh.template_id = ?
            ON CONFLICT DO NOTHING
        ", [$company_id, $template_id]);
        
        // 2. Copy document heads from template to sub_type for this company
        $this->db->query("
            INSERT INTO sub_type (type_name, alias_name, authority_id, frequency, frequency_start_date, 
                document_name, document_start_date, due_in_same_next_month, status, company_id, is_enabled, created_by)
            SELECT st.type_name, st.alias_name, 
                (SELECT id FROM authority WHERE company_id = ? AND authority_name = (
                    SELECT authority_name FROM authority WHERE id::text = st.authority_id
                ))::text,
                st.frequency, st.frequency_start_date, st.document_name, st.document_start_date,
                st.due_in_same_next_month, '1', ?, TRUE, ?
            FROM company_template_document_heads ctdh
            JOIN sub_type st ON st.id = ctdh.type_id
            WHERE ctdh.template_id = ?
            ON CONFLICT DO NOTHING
        ", [$company_id, $company_id, $created_by, $template_id]);
        
        // 3. Copy documents from template to documents table for this company
        $this->db->query("
            INSERT INTO documents (document_name, company_id, status)
            SELECT DISTINCT d.document_name, ?, '1'
            FROM mandatory_documents md
            JOIN documents d ON d.id = md.document_id
            JOIN company_template_document_heads ctdh ON md.type_id = ctdh.type_id
            WHERE ctdh.template_id = ?
            ON CONFLICT DO NOTHING
        ", [$company_id, $template_id]);
        
        // 4. Copy mandatory documents
        $this->db->query("
            INSERT INTO mandatory_documents (type_id, document_id, mandatory, company_id, is_enabled)
            SELECT 
                (SELECT id FROM sub_type WHERE company_id = ? AND type_name = (SELECT type_name FROM sub_type WHERE id = md.type_id)),
                (SELECT id FROM documents WHERE company_id = ? AND document_name = (SELECT document_name FROM documents WHERE id = md.document_id)),
                md.mandatory, ?, TRUE
            FROM mandatory_documents md
            JOIN company_template_document_heads ctdh ON md.type_id = ctdh.type_id
            WHERE ctdh.template_id = ?
            ON CONFLICT DO NOTHING
        ", [$company_id, $company_id, $company_id, $template_id]);
        
        // 5. Copy reminders to sub_type_reminders for this company
        $this->db->query("
            INSERT INTO sub_type_reminders (sub_type_id, type_id, company_id, reminder_no, days_before, 
                reminder_to_user, reminder_to_admin, reminder_to_super_admin, is_enabled)
            SELECT 
                (SELECT id FROM sub_type WHERE company_id = ? AND type_name = (SELECT type_name FROM sub_type WHERE id = sr.sub_type_id)),
                (SELECT id FROM sub_type WHERE company_id = ? AND type_name = (SELECT type_name FROM sub_type WHERE id = sr.sub_type_id)),
                ?, sr.reminder_no, sr.days_before,
                sr.reminder_to_user, sr.reminder_to_admin, sr.reminder_to_super_admin, TRUE
            FROM sub_type_reminders sr
            JOIN company_template_document_heads ctdh ON sr.sub_type_id = ctdh.type_id
            WHERE ctdh.template_id = ?
            ON CONFLICT DO NOTHING
        ", [$company_id, $company_id, $company_id, $template_id]);
        
        $this->db->trans_complete();
        
        return $this->db->trans_status();
    }
    
    /**
     * Count pending (disabled) document heads for a company
     * Updated to use is_enabled directly on sub_type
     * 
     * @param int $company_id The company ID
     * @return int Count of pending document heads
     */
    public function count_pending_doc_heads($company_id) {
        // Count disabled document heads for this company directly from sub_type
        $count = $this->db->where('company_id', $company_id)
            ->where('is_enabled', false)
            ->where('status', '1')
            ->count_all_results('sub_type');
        
        return $count;
    }
    
    /**
     * Count new document heads in master that are not yet added to company
     * NOTE: With company-wise masters, this always returns 0 as document heads are created per company
     * 
     * @param int $company_id The company ID
     * @return int Count of new document heads not in company (always 0 with company-wise masters)
     */
    public function count_new_master_doc_heads($company_id) {
        // With company-wise masters, each company creates its own document heads
        // There is no "master" template to compare against
        return 0;
    }
    
    /**
     * Get total pending items count for company doc heads
     * (disabled items + new master items not added)
     * 
     * @param int $company_id The company ID
     * @return int Total pending count
     */
    public function get_pending_doc_heads_count($company_id) {
        $disabled_count = $this->count_pending_doc_heads($company_id);
        $new_count = $this->count_new_master_doc_heads($company_id);
        
        return $disabled_count + $new_count;
    }
    
    /**
     * Get pending doc heads count for all companies user has access to
     * 
     * @param array $company_ids Array of company IDs user can access
     * @return int Total pending count across all accessible companies
     */
    public function get_total_pending_doc_heads($company_ids) {
        if (empty($company_ids)) {
            return 0;
        }
        
        $total = 0;
        foreach ($company_ids as $company_id) {
            $total += $this->get_pending_doc_heads_count($company_id);
        }
        
        return $total;
    }

    // =====================================================
    // USER PERMISSION METHODS (Per-user View/Add-Edit/Delete)
    // =====================================================

    /**
     * Get user permissions (can_view, can_add_edit, can_delete)
     * 
     * @param int $user_id The user ID
     * @return object|null Permission object or null if not found
     */
    public function get_user_permissions($user_id) {
        $row = $this->db->where('user_id', $user_id)
            ->get('user_permissions')
            ->row();
        
        // Convert PostgreSQL boolean strings ('t'/'f') to PHP booleans
        if ($row) {
            $row->can_view = ($row->can_view === true || $row->can_view === 't' || $row->can_view === '1' || $row->can_view === 1);
            $row->can_add_edit = ($row->can_add_edit === true || $row->can_add_edit === 't' || $row->can_add_edit === '1' || $row->can_add_edit === 1);
            $row->can_delete = ($row->can_delete === true || $row->can_delete === 't' || $row->can_delete === '1' || $row->can_delete === 1);
        }
        
        return $row;
    }

    /**
     * Check if user can view content
     * 
     * @param int $user_id The user ID
     * @param int $role_id The user's role ID
     * @return bool True if user can view
     */
    public function can_user_view($user_id, $role_id) {
        // Super Admin always has full access
        if ($role_id == '1') {
            return true;
        }
        
        $perms = $this->get_user_permissions($user_id);
        return $perms ? (bool)$perms->can_view : true; // Default to true for view
    }

    /**
     * Check if user can add/edit content
     * 
     * @param int $user_id The user ID
     * @param int $role_id The user's role ID
     * @return bool True if user can add/edit
     */
    public function can_user_edit($user_id, $role_id) {
        // Super Admin always has full access
        if ($role_id == '1') {
            return true;
        }
        
        $perms = $this->get_user_permissions($user_id);
        return $perms ? (bool)$perms->can_add_edit : false;
    }

    /**
     * Check if user can delete content
     * 
     * @param int $user_id The user ID
     * @param int $role_id The user's role ID
     * @return bool True if user can delete
     */
    public function can_user_delete($user_id, $role_id) {
        // Super Admin always has full access
        if ($role_id == '1') {
            return true;
        }
        
        // Users (role 3) can never delete regardless of setting
        if ($role_id == '3') {
            return false;
        }
        
        $perms = $this->get_user_permissions($user_id);
        return $perms ? (bool)$perms->can_delete : false;
    }

    /**
     * Save or update user permissions
     * 
     * @param int $user_id The user ID
     * @param bool $can_view Can view permission
     * @param bool $can_add_edit Can add/edit permission
     * @param bool $can_delete Can delete permission
     * @return bool Success status
     */
    public function save_user_permissions($user_id, $can_view = true, $can_add_edit = false, $can_delete = false) {
        $data = [
            'user_id' => $user_id,
            'can_view' => $can_view ? true : false,
            'can_add_edit' => $can_add_edit ? true : false,
            'can_delete' => $can_delete ? true : false,
            'updated_at' => date('Y-m-d H:i:s')
        ];
        
        // Check if permissions exist
        $existing = $this->get_user_permissions($user_id);
        
        if ($existing) {
            unset($data['user_id']);
            return $this->db->where('user_id', $user_id)
                ->update('user_permissions', $data);
        } else {
            $data['created_at'] = date('Y-m-d H:i:s');
            return $this->db->insert('user_permissions', $data);
        }
    }

    /**
     * Delete user permissions (when user is deleted)
     * 
     * @param int $user_id The user ID
     * @return bool Success status
     */
    public function delete_user_permissions($user_id) {
        return $this->db->where('user_id', $user_id)
            ->delete('user_permissions');
    }

    /**
     * Get document heads by authority IDs
     * Used for auto-populating doc heads when authorities are selected for User role
     * 
     * @param array $authority_ids Array of authority IDs
     * @return array Array of document heads
     */
    public function get_document_heads_by_authorities($authority_ids) {
        if (empty($authority_ids)) {
            return [];
        }
        
        return $this->db->select('st.id, st.type_name, st.authority_id, a.authority_name')
            ->from('sub_type st')
            ->join('authority a', 'a.id::text = st.authority_id', 'left')
            ->where('st.status', '1')
            ->where_in('st.authority_id', $authority_ids)
            ->order_by('a.authority_name, st.type_name')
            ->get()
            ->result_array();
    }

    /**
     * Save user authorities to normalized table
     * 
     * @param int $user_id The user ID
     * @param array $authority_ids Array of authority IDs
     * @return bool Success status
     */
    public function save_user_authorities($user_id, $authority_ids) {
        // Delete existing
        $this->db->where('user_id', $user_id)->delete('user_authorities');
        
        if (empty($authority_ids)) {
            return true;
        }
        
        // Insert new
        $data = [];
        foreach ($authority_ids as $authority_id) {
            $data[] = [
                'user_id' => $user_id,
                'authority_id' => (int)$authority_id
            ];
        }
        
        return $this->db->insert_batch('user_authorities', $data);
    }

    /**
     * Save user companies to normalized table
     * 
     * @param int $user_id The user ID
     * @param array $company_ids Array of company IDs
     * @return bool Success status
     */
    public function save_user_companies($user_id, $company_ids) {
        // Delete existing
        $this->db->where('user_id', $user_id)->delete('user_companies');
        
        if (empty($company_ids)) {
            return true;
        }
        
        // Insert new
        $data = [];
        foreach ($company_ids as $company_id) {
            $data[] = [
                'user_id' => $user_id,
                'company_id' => (int)$company_id
            ];
        }
        
        return $this->db->insert_batch('user_companies', $data);
    }

    /**
     * Save user document types to normalized table
     * 
     * @param int $user_id The user ID
     * @param array $type_ids Array of document type IDs
     * @return bool Success status
     */
    public function save_user_document_types($user_id, $type_ids) {
        // Delete existing
        $this->db->where('user_id', $user_id)->delete('user_document_types');
        
        if (empty($type_ids)) {
            return true;
        }
        
        // Insert new
        $data = [];
        foreach ($type_ids as $type_id) {
            $data[] = [
                'user_id' => $user_id,
                'type_id' => (int)$type_id
            ];
        }
        
        return $this->db->insert_batch('user_document_types', $data);
    }
}

