<?php
/**
 * Account Controller — Chart of Accounts (Parties, Banks, System)
 */

class AccountController {
    private PDO $db;
    private object $user;

    public function __construct(PDO $db, object $user) {
        $this->db = $db;
        $this->user = $user;
    }

    /** GET /accounts?type=party&sub_type=zamidar */
    public function index(array $query): void {
        $where = ['1=1'];
        $params = [];

        if (!empty($query['type'])) {
            $where[] = 'a.type = ?';
            $params[] = $query['type'];
        }
        if (!empty($query['sub_type'])) {
            $where[] = 'a.sub_type = ?';
            $params[] = $query['sub_type'];
        }
        if (isset($query['is_active'])) {
            $where[] = 'a.is_active = ?';
            $params[] = (int)$query['is_active'];
        } else {
            $where[] = 'a.is_active = 1';
        }

        if (isset($query['exclude_equity']) && $query['exclude_equity'] == '1') {
            $where[] = "!(a.type = 'capital' AND a.is_system = 1)";
        }

        if (isset($query['exclude_capital']) && $query['exclude_capital'] == '1') {
            $where[] = "a.type != 'capital' AND a.name NOT LIKE 'capital (سرمایہ)%'";
        }

        if (isset($query['include_balance']) && $query['include_balance'] == '1') {
            $sql = "SELECT a.*, 
                           COALESCE(SUM(CASE WHEN j.is_reversed = 0 AND j.narration NOT LIKE 'REVERSAL%' THEN je.debit ELSE 0 END), 0) as total_dr,
                           COALESCE(SUM(CASE WHEN j.is_reversed = 0 AND j.narration NOT LIKE 'REVERSAL%' THEN je.credit ELSE 0 END), 0) as total_cr
                    FROM accounts a
                    LEFT JOIN journal_entries je ON je.account_id = a.id
                    LEFT JOIN journals j ON je.journal_id = j.id
                    WHERE " . implode(' AND ', $where) . "
                    GROUP BY a.id
                    ORDER BY a.name ASC";
        } else {
            $sql = "SELECT a.* FROM accounts a WHERE " . implode(' AND ', $where) . " ORDER BY a.name ASC";
        }

        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);

        Response::success($stmt->fetchAll());
    }

    /** POST /accounts */
    public function store(array $data): void {
        Validator::check($data, [
            'name'            => 'required|string|max:255',
            'type'            => 'required|in:party,bank_cash,system_income,system_expense,capital',
            'sub_type'        => 'nullable|string|max:50',
            'phone'           => 'nullable|string|max:20',
            'city'            => 'nullable|string|max:100',
            'opening_balance' => 'nullable|numeric|non_negative',
            'balance_type'    => 'nullable|in:Dr,Cr',
            'cycle_start_date' => 'nullable|date',
            'alert_interval_days' => 'nullable|integer|non_negative',
            'allow_direct_deposit' => 'nullable|integer|in:0,1'
        ]);

        $cycleStartDate = !empty($data['cycle_start_date']) ? $data['cycle_start_date'] : null;
        $intervalDays = (int)($data['alert_interval_days'] ?? 0);
        $alertDate = null;

        // Auto-calculate alert date: Cycle Start Date + Interval
        if ($cycleStartDate && $intervalDays > 0) {
            $alertDate = date('Y-m-d', strtotime("$cycleStartDate +$intervalDays days"));
        }

        $this->db->beginTransaction();
        try {
            $stmt = $this->db->prepare("
                INSERT INTO accounts (name, type, sub_type, phone, city, opening_balance, balance_type, cycle_start_date, alert_date, alert_interval_days, allow_direct_deposit)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            ");
            $stmt->execute([
                $data['name'],
                $data['type'],
                $data['sub_type'] ?? null,
                $data['phone'] ?? null,
                $data['city'] ?? null,
                floatval($data['opening_balance'] ?? 0),
                $data['balance_type'] ?? 'Dr',
                $cycleStartDate,
                $alertDate,
                $intervalDays,
                (int)($data['allow_direct_deposit'] ?? 0)
            ]);

            $accountId = (int)$this->db->lastInsertId();

            // If opening balance > 0, create an opening balance journal entry
            $openingBal = floatval($data['opening_balance'] ?? 0);
            if ($openingBal > 0) {
                $balType = $data['balance_type'] ?? 'Dr';
                $entries = [];

                if ($balType === 'Dr') {
                    $entries[] = ['account_id' => $accountId, 'debit' => $openingBal, 'credit' => 0, 'narration' => 'Opening Balance (Dr)'];
                    // Credit to a suspense/equity (we'll use the opening balance as a self-balancing entry)
                    // In practice, for opening balances, we create a mirror entry
                    $entries[] = ['account_id' => $accountId, 'debit' => 0, 'credit' => $openingBal, 'narration' => 'Opening Balance Offset'];
                } else {
                    $entries[] = ['account_id' => $accountId, 'debit' => $openingBal, 'credit' => 0, 'narration' => 'Opening Balance Offset'];
                    $entries[] = ['account_id' => $accountId, 'debit' => 0, 'credit' => $openingBal, 'narration' => 'Opening Balance (Cr)'];
                }

                // Note: Opening balance is stored directly on the account record.
                // We DON'T create a journal for it — it's used as a starting point in ledger calculations.
                // This keeps the journal clean for operational transactions only.
            }

            AuditLogger::log($this->db, $this->user->id, 'CREATED_ACCOUNT', 'accounts', $accountId, null, $data);
            $this->db->commit();

            Response::created(['id' => $accountId], 'اکاؤنٹ کامیابی سے بنایا گیا۔');
        } catch (Exception $e) {
            $this->db->rollBack();
            throw $e;
        }
    }

    /** PUT /accounts/{id} */
    public function update(int $id, array $data): void {
        $stmt = $this->db->prepare("SELECT * FROM accounts WHERE id = ?");
        $stmt->execute([$id]);
        $existing = $stmt->fetch();
        if (!$existing) Response::notFound('اکاؤنٹ نہیں ملا۔');

        // Only allow updating non-financial fields
        $fields = [];
        $params = [];

        if (isset($data['name'])) { $fields[] = 'name = ?'; $params[] = $data['name']; }
        if (isset($data['sub_type'])) { $fields[] = 'sub_type = ?'; $params[] = $data['sub_type']; }
        if (isset($data['phone'])) { $fields[] = 'phone = ?'; $params[] = $data['phone']; }
        if (isset($data['city'])) { $fields[] = 'city = ?'; $params[] = $data['city']; }
        if (isset($data['allow_direct_deposit'])) { $fields[] = 'allow_direct_deposit = ?'; $params[] = (int)$data['allow_direct_deposit']; }
        
        // Auto-calculate alert date for updates too
        $cycleStartDate = isset($data['cycle_start_date']) ? $data['cycle_start_date'] : $existing['cycle_start_date'];
        $intervalDays = isset($data['alert_interval_days']) ? (int)$data['alert_interval_days'] : (int)$existing['alert_interval_days'];
        $alertDate = $existing['alert_date']; // Default to existing

        if ($cycleStartDate && $intervalDays > 0) {
            $alertDate = date('Y-m-d', strtotime("$cycleStartDate +$intervalDays days"));
        }

        if (array_key_exists('cycle_start_date', $data)) { $fields[] = 'cycle_start_date = ?'; $params[] = $cycleStartDate; }
        if (array_key_exists('alert_interval_days', $data) || array_key_exists('cycle_start_date', $data)) {
            $fields[] = 'alert_date = ?'; $params[] = $alertDate;
            $fields[] = 'alert_interval_days = ?'; $params[] = $intervalDays;
        }

        if (isset($data['is_active'])) { $fields[] = 'is_active = ?'; $params[] = (int)$data['is_active']; }

        if (empty($fields)) {
            Response::success(null, 'کوئی تبدیلی نہیں کی گئی۔');
            return;
        }

        $params[] = $id;
        $sql = "UPDATE accounts SET " . implode(', ', $fields) . " WHERE id = ?";
        $this->db->prepare($sql)->execute($params);

        AuditLogger::log($this->db, $this->user->id, 'UPDATED_ACCOUNT', 'accounts', $id, $existing, $data);

        Response::success(null, 'اکاؤنٹ اپڈیٹ ہو گیا۔');
    }

    /** DELETE /accounts/{id} */
    public function destroy(int $id): void {
        $stmt = $this->db->prepare("SELECT * FROM accounts WHERE id = ?");
        $stmt->execute([$id]);
        $existing = $stmt->fetch();

        if (!$existing) Response::notFound('اکاؤنٹ نہیں ملا۔');
        if ($existing['is_system']) Response::error('سسٹم اکاؤنٹس حذف نہیں کیے جا سکتے۔', 403);

        // Check if there are any transactions associated with this account
        $stmtCount = $this->db->prepare("SELECT COUNT(*) FROM journal_entries WHERE account_id = ?");
        $stmtCount->execute([$id]);
        $hasEntries = (int)$stmtCount->fetchColumn();

        if ($hasEntries > 0) {
            Response::error('یہ اکاؤنٹ استعمال ہو چکا ہے اور اس میں ٹرانزیکشنز موجود ہیں۔ اس لیے اسے حذف نہیں کیا جا سکتا۔', 400);
        }

        // Soft delete: set is_active = 0
        $this->db->prepare("UPDATE accounts SET is_active = 0 WHERE id = ?")->execute([$id]);

        AuditLogger::log($this->db, $this->user->id, 'DELETED_ACCOUNT', 'accounts', $id, $existing, ['is_active' => 0]);

        Response::success(null, 'اکاؤنٹ کامیابی سے حذف ہو گیا۔');
    }

    /** POST /accounts/{id}/renew-alert */
    public function renewAlert(int $id): void {
        $stmt = $this->db->prepare("SELECT alert_date, alert_interval_days FROM accounts WHERE id = ?");
        $stmt->execute([$id]);
        $acc = $stmt->fetch();

        if (!$acc) Response::notFound('اکاؤنٹ نہیں ملا۔');
        if (!$acc['alert_date'] || !$acc['alert_interval_days']) {
            Response::error('اس اکاؤنٹ کیلئے تکراری الرٹ فعال نہیں ہے۔', 400);
        }

        // Calculate next date: current alert_date + interval
        $currentDate = new DateTime($acc['alert_date']);
        $interval = new DateInterval('P' . $acc['alert_interval_days'] . 'D');
        $currentDate->add($interval);
        $nextDate = $currentDate->format('Y-m-d');

        $this->db->prepare("UPDATE accounts SET alert_date = ? WHERE id = ?")->execute([$nextDate, $id]);

        Response::success(['next_date' => $nextDate], "الرٹ اگلی تاریخ ({$nextDate}) پر منتقل کر دیا گیا ہے۔");
    }
}
