<?php
/**
 * Arag_Shop_Accounting — حسابداری جمع‌وجور فروش فروشگاه (دفتر کل · اسناد · گزارش)
 * همه اسناد با source_module=arag_shop و event_type=SHOP_SALE در GL ثبت می‌شوند.
 */
if ( ! defined( 'ABSPATH' ) ) exit;

require_once ACC_PATH . 'includes/helpers/arag-erp.php';

class Arag_Shop_Accounting {

    public const MODULE     = 'arag_shop';
    public const EVENT_TYPE = 'SHOP_SALE';

    public static function meta(): array {
        $portal = Arag_Erp::portal_settings();
        return [
            'module'          => self::MODULE,
            'event_type'      => self::EVENT_TYPE,
            'ledger_tag'      => $portal['ledger_tag'] ?? 'SHOP',
            'office_code'     => $portal['office_code'] ?? 'ARAG-01',
            'office_name'     => $portal['office_name'] ?? 'فروشگاه عرقیات',
            'subsystem_label' => 'حسابداری فروش فروشگاه',
            'description'     => 'دفتر کل · اسناد · تراز · سود و زیان — مخصوص فروش آنلاین عرقیات',
        ];
    }

    /** فیلتر SQL رویدادهای فروشگاه */
    public static function shop_event_where( string $alias = 'e' ): string {
        $parts = [
            "{$alias}.event_type = '" . self::EVENT_TYPE . "'",
            "{$alias}.reference_type = 'arag_order'",
        ];
        if ( self::has_col( 'ai_acc_events', 'source_module' ) ) {
            $parts[] = "{$alias}.source_module = '" . self::MODULE . "'";
        }
        return '(' . implode( ' OR ', $parts ) . ')';
    }

    private static function has_col( string $table, string $col ): bool {
        if ( ! class_exists( 'Acc_Event_Engine' ) ) {
            require_once ACC_PATH . 'includes/helpers/acc-event-engine.php';
        }
        return Acc_Event_Engine::has_column( $table, $col );
    }

    /** تأیید نهایی سند (is_confirmed=1) */
    public static function confirm_event( int $id ): array {
        global $wpdb;
        $pre = $wpdb->prefix;

        $state = (string) $wpdb->get_var( $wpdb->prepare(
            "SELECT workflow_state FROM {$pre}ai_acc_events WHERE id = %d", $id
        ) );
        if ( $state === '' ) {
            return [ 'success' => false, 'message' => 'رویداد یافت نشد', 'http' => 404 ];
        }
        if ( $state === 'confirmed' ) {
            return [ 'success' => true, 'message' => 'قبلاً تأیید شده', 'already' => true ];
        }
        if ( $state !== 'posted' ) {
            return [ 'success' => false, 'message' => 'فقط سند پست‌شده قابل تأیید است (وضعیت: ' . $state . ')', 'http' => 400 ];
        }

        $wpdb->query( $wpdb->prepare(
            "UPDATE {$pre}ai_acc_flows SET is_confirmed = 1 WHERE event_id = %d", $id
        ) );
        $upd = [ 'workflow_state' => 'confirmed' ];
        if ( self::has_col( 'ai_acc_events', 'status' ) ) {
            $upd['status'] = 'POSTED';
        }
        $wpdb->update( $pre . 'ai_acc_events', $upd, [ 'id' => $id ] );

        return [ 'success' => true, 'event_id' => $id ];
    }

    public static function dashboard(): array {
        global $wpdb;
        $pre   = $wpdb->prefix;
        $where = self::shop_event_where( 'e' );
        $meta  = self::meta();

        $pending = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$pre}ai_acc_events e
             WHERE {$where} AND e.workflow_state IN ('pending','failed')"
        );
        $posted = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$pre}ai_acc_events e
             WHERE {$where} AND e.workflow_state = 'posted'"
        );
        $confirmed = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$pre}ai_acc_events e
             WHERE {$where} AND e.workflow_state = 'confirmed'"
        );

        $month_start = date( 'Y-m-01' );
        $month_end   = date( 'Y-m-t' );
        $month_rev = self::_sum_account_type( 'revenue', $month_start, $month_end );
        $month_exp = self::_sum_account_type( 'expense', $month_start, $month_end );

        if ( class_exists( 'Arag_Expense' ) ) {
            require_once ACC_PATH . 'includes/helpers/arag-expense.php';
            $op_exp = Arag_Expense::expense_summary( $month_start, $month_end );
            $month_exp += (float) ( $op_exp['total'] ?? 0 );
        }

        $balances = self::_account_balances( null, null, true );
        $cash = 0.0;
        foreach ( $balances as $b ) {
            if ( in_array( $b['account_type'], [ 'asset', 'bank' ], true ) || in_array( (int) $b['id'], [ Arag_Erp::GL['bank'], Arag_Erp::GL['ar'] ], true ) ) {
                $cash += (float) $b['total_debit'] - (float) $b['total_credit'];
            }
        }

        return [
            'meta'    => $meta,
            'stats'   => [
                'pending'         => $pending,
                'posted'          => $posted,
                'confirmed'       => $confirmed,
                'month_revenue'   => $month_rev,
                'month_expense'   => $month_exp,
                'month_profit'    => $month_rev - $month_exp,
                'cash_position'   => $cash,
                'queue_orders'    => count( Arag_Erp::pending_accounting_queue( 100 ) ),
            ],
            'period'  => [ 'from' => $month_start, 'to' => $month_end ],
        ];
    }

    public static function list_documents( int $limit = 80, string $state = '' ): array {
        global $wpdb;
        $pre   = $wpdb->prefix;
        $where = self::shop_event_where( 'e' );
        $args  = [];

        if ( $state !== '' ) {
            $where .= ' AND e.workflow_state = %s';
            $args[] = sanitize_text_field( $state );
        }

        $src_col = self::has_col( 'ai_acc_events', 'source_module' ) ? ', e.source_module' : ", '" . self::MODULE . "' AS source_module";
        $sql = "SELECT e.id, e.event_type, e.workflow_state, e.amount, e.created_at,
                       e.reference_type, e.reference_id {$src_col},
                       o.id AS order_id, o.order_number, o.billing_name, o.payment_status, o.subtotal
                FROM {$pre}ai_acc_events e
                LEFT JOIN {$pre}arag_orders o ON (
                    o.acc_event_id = e.id
                    OR (e.reference_type = 'arag_order' AND e.reference_id = o.id)
                )
                WHERE {$where}
                ORDER BY e.id DESC
                LIMIT %d";
        $args[] = $limit;

        $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$args ), ARRAY_A ) ?: [];
        foreach ( $rows as &$r ) {
            $r['ledger_tag'] = self::meta()['ledger_tag'];
            $r['module_label'] = self::meta()['subsystem_label'];
        }
        return $rows;
    }

    public static function event_detail( int $event_id ): ?array {
        global $wpdb;
        $pre = $wpdb->prefix;
        $where = self::shop_event_where( 'e' ) . ' AND e.id = %d';
        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT e.* FROM {$pre}ai_acc_events e WHERE {$where}",
            $event_id
        ), ARRAY_A );
        if ( ! $row ) {
            return null;
        }
        $row['flows'] = self::event_flows( $event_id );
        $row['meta']  = self::meta();
        return $row;
    }

    public static function event_flows( int $event_id ): array {
        global $wpdb;
        $pre = $wpdb->prefix;
        return $wpdb->get_results( $wpdb->prepare(
            "SELECT f.id AS flow_id, f.event_id, f.from_account_id, f.to_account_id, f.amount,
                    f.description, f.is_confirmed, f.created_at,
                    fa.account_code AS from_code, fa.account_name AS from_name,
                    ta.account_code AS to_code, ta.account_name AS to_name
             FROM {$pre}ai_acc_flows f
             INNER JOIN {$pre}ai_acc_events e ON e.id = f.event_id AND " . self::shop_event_where( 'e' ) . "
             LEFT JOIN {$pre}ai_acc_accounts fa ON fa.account_id = f.from_account_id
             LEFT JOIN {$pre}ai_acc_accounts ta ON ta.account_id = f.to_account_id
             WHERE f.event_id = %d
             ORDER BY f.line_no ASC, f.id ASC",
            $event_id
        ), ARRAY_A ) ?: [];
    }

    public static function ledger( ?string $from, ?string $to, int $limit = 200 ): array {
        global $wpdb;
        $pre   = $wpdb->prefix;
        $conds = [ self::shop_event_where( 'e' ), 'f.is_confirmed = 1' ];
        $args  = [];

        if ( $from ) {
            $conds[] = 'f.created_at >= %s';
            $args[]  = sanitize_text_field( $from ) . ' 00:00:00';
        }
        if ( $to ) {
            $conds[] = 'f.created_at <= %s';
            $args[]  = sanitize_text_field( $to ) . ' 23:59:59';
        }
        $args[] = $limit;

        $sql = "SELECT f.id, f.event_id, f.amount, f.description, f.is_confirmed, f.created_at,
                       e.event_type, e.workflow_state,
                       fa.account_code AS dr_code, fa.account_name AS dr_name,
                       ta.account_code AS cr_code, ta.account_name AS cr_name,
                       o.order_number
                FROM {$pre}ai_acc_flows f
                INNER JOIN {$pre}ai_acc_events e ON e.id = f.event_id
                LEFT JOIN {$pre}ai_acc_accounts fa ON fa.account_id = f.from_account_id
                LEFT JOIN {$pre}ai_acc_accounts ta ON ta.account_id = f.to_account_id
                LEFT JOIN {$pre}arag_orders o ON o.acc_event_id = e.id
                WHERE " . implode( ' AND ', $conds ) . "
                ORDER BY f.created_at DESC, f.id DESC
                LIMIT %d";

        return $wpdb->get_results( $wpdb->prepare( $sql, ...$args ), ARRAY_A ) ?: [];
    }

    public static function trial_balance( ?string $from, ?string $to ): array {
        $rows = self::_account_balances( $from, $to, true );
        $total_dr = 0.0;
        $total_cr = 0.0;
        foreach ( $rows as &$r ) {
            $r['net_balance'] = (float) $r['total_debit'] - (float) $r['total_credit'];
            $total_dr += (float) $r['total_debit'];
            $total_cr += (float) $r['total_credit'];
        }
        return [
            'rows'      => $rows,
            'total_dr'  => $total_dr,
            'total_cr'  => $total_cr,
            'balanced'  => abs( $total_dr - $total_cr ) < 0.01,
            'meta'      => self::meta(),
        ];
    }

    public static function income_statement( ?string $from, ?string $to ): array {
        $rows = self::_account_balances( $from, $to, true );

        $revenue = [];
        $contra  = [];
        $expense = [];
        $rev_t = 0.0;
        $con_t = 0.0;
        $exp_t = 0.0;

        foreach ( $rows as $r ) {
            $dr = (float) $r['total_debit'];
            $cr = (float) $r['total_credit'];
            $line = [ 'code' => $r['code'], 'name' => $r['name'], 'source' => 'gl' ];
            switch ( $r['account_type'] ) {
                case 'revenue':
                    $line['amount'] = $cr - $dr;
                    $revenue[] = $line;
                    $rev_t += $line['amount'];
                    break;
                case 'contra':
                    $line['amount'] = $dr - $cr;
                    $contra[] = $line;
                    $con_t += $line['amount'];
                    break;
                case 'expense':
                    $line['amount'] = $dr - $cr;
                    $expense[] = $line;
                    $exp_t += $line['amount'];
                    break;
            }
        }

        $operational = [];
        $op_total = 0.0;
        if ( class_exists( 'Arag_Expense' ) ) {
            require_once ACC_PATH . 'includes/helpers/arag-expense.php';
            $items = Arag_Expense::list_expenses( 500, $from, $to );
            $cats  = Arag_Expense::categories();
            $by_cat = [];
            foreach ( $items as $e ) {
                $c = $e['category'] ?? 'other';
                $by_cat[ $c ] = ( $by_cat[ $c ] ?? 0 ) + (float) $e['amount'];
            }
            foreach ( $by_cat as $key => $amt ) {
                if ( $amt <= 0 ) continue;
                $label = $cats[ $key ]['label'] ?? $key;
                $operational[] = [
                    'code'   => 'OP-' . strtoupper( $key ),
                    'name'   => $label . ' (هزینه عملیاتی)',
                    'amount' => $amt,
                    'source' => 'operational',
                ];
                $op_total += $amt;
            }
            $exp_t += $op_total;
        }

        $net = $rev_t - $con_t - $exp_t;

        return [
            'revenue'     => $revenue,
            'contra'      => $contra,
            'expense_gl'  => $expense,
            'expense_op'  => $operational,
            'totals'      => [
                'revenue'       => $rev_t,
                'contra'        => $con_t,
                'expense_gl'    => $exp_t - $op_total,
                'expense_op'    => $op_total,
                'expense_total' => $exp_t,
                'net_income'    => $net,
            ],
            'meta'        => self::meta(),
            'period'      => [ 'from' => $from, 'to' => $to ],
        ];
    }

    public static function chart_accounts(): array {
        require_once ACC_PATH . 'includes/helpers/acc-portal-accounting.php';
        Acc_Portal_Accounting::ensure_chart( self::MODULE );
        return Acc_Portal_Accounting::chart_tree( self::MODULE );
    }

    private static function _sum_account_type( string $type, ?string $from, ?string $to ): float {
        $rows = self::_account_balances( $from, $to, true );
        $sum = 0.0;
        foreach ( $rows as $r ) {
            if ( ( $r['account_type'] ?? '' ) !== $type ) {
                continue;
            }
            $dr = (float) $r['total_debit'];
            $cr = (float) $r['total_credit'];
            $sum += $type === 'revenue' ? ( $cr - $dr ) : ( $dr - $cr );
        }
        return $sum;
    }

    private static function _account_balances( ?string $from, ?string $to, bool $confirmed_only ): array {
        global $wpdb;
        $pre = $wpdb->prefix;

        $conds = [ self::shop_event_where( 'e' ) ];
        if ( $confirmed_only ) {
            $conds[] = 'f.is_confirmed = 1';
        }
        if ( $from ) {
            $conds[] = 'f.created_at >= %s';
        }
        if ( $to ) {
            $conds[] = 'f.created_at <= %s';
        }
        $args = [];
        if ( $from ) {
            $args[] = sanitize_text_field( $from ) . ' 00:00:00';
        }
        if ( $to ) {
            $args[] = sanitize_text_field( $to ) . ' 23:59:59';
        }
        $fwhere = implode( ' AND ', $conds );

        $sql = "SELECT a.account_id AS id, a.account_code AS code, a.account_name AS name, a.account_type,
                       COALESCE(SUM(CASE WHEN f.from_account_id = a.account_id THEN f.amount ELSE 0 END), 0) AS total_debit,
                       COALESCE(SUM(CASE WHEN f.to_account_id   = a.account_id THEN f.amount ELSE 0 END), 0) AS total_credit
                FROM {$pre}ai_acc_accounts a
                INNER JOIN {$pre}ai_acc_flows f
                    ON (f.from_account_id = a.account_id OR f.to_account_id = a.account_id)
                INNER JOIN {$pre}ai_acc_events e ON e.id = f.event_id AND {$fwhere}
                GROUP BY a.account_id, a.account_code, a.account_name, a.account_type
                HAVING total_debit <> 0 OR total_credit <> 0
                ORDER BY a.account_code ASC";

        return $args
            ? ( $wpdb->get_results( $wpdb->prepare( $sql, ...$args ), ARRAY_A ) ?: [] )
            : ( $wpdb->get_results( $sql, ARRAY_A ) ?: [] );
    }

    /** backfill source_module برای اسناد قدیمی */
    public static function migrate_legacy_events(): array {
        global $wpdb;
        $pre = $wpdb->prefix;
        if ( ! self::has_col( 'ai_acc_events', 'source_module' ) ) {
            return [ 'updated' => 0, 'message' => 'ستون source_module وجود ندارد' ];
        }
        $n = (int) $wpdb->query(
            "UPDATE {$pre}ai_acc_events
             SET source_module = '" . self::MODULE . "'
             WHERE (event_type = '" . self::EVENT_TYPE . "' OR reference_type = 'arag_order')
               AND (source_module IS NULL OR source_module = '')"
        );
        return [ 'updated' => $n ];
    }
}
