<?php
/**
 * election-v7-finance-segments.php — مالی ستاد + import 1011 پهنه + SMS OTP
 */
if ( ! defined( 'ABSPATH' ) ) exit;

trait Election_V7_Finance_Segments {

    public const SEGMENT_TARGET = 1011;

    public const BUDGET_CATEGORIES = [
        'media'        => 'تبلیغات و رسانه',
        'distribution' => 'توزیع و چاپ',
        'goods'        => 'اقلام تبلیغاتی',
        'field'        => 'عملیات میدانی',
        'staff'        => 'نیروی انسانی',
        'logistics'    => 'اداری و پشتیبانی',
        'tech'         => 'فناوری و GIS',
        'legal'        => 'حقوقی',
        'other'        => 'سایر',
    ];

    public static function budget_table(): string {
        global $wpdb;
        return $wpdb->prefix . 'election_budget';
    }

    public static function transactions_table(): string {
        global $wpdb;
        return $wpdb->prefix . 'election_transactions';
    }

    public static function fin_audit_table(): string {
        global $wpdb;
        return $wpdb->prefix . 'election_fin_audit';
    }

    public static function otp_demo_mode(): bool {
        return (string) get_option( 'acc_election_otp_demo', '1' ) === '1';
    }

    public static function sms_config(): array {
        require_once ACC_PATH . 'includes/helpers/acc-sms.php';
        $cfg = Acc_SMS::config();
        return [
            'provider'     => $cfg['provider'],
            'sender'       => $cfg['sender'],
            'otp_template' => $cfg['otp_template'],
            'has_api_key'  => (bool) $cfg['api_key'],
            'otp_demo'     => self::otp_demo_mode(),
        ];
    }

    public static function save_sms_config( int $uid, array $data ): array {
        if ( ! self::can_manage( $uid ) ) {
            return [ 'success' => false, 'message' => 'دسترسی ندارید' ];
        }
        if ( isset( $data['sms_provider'] ) ) {
            update_option( 'acc_sms_provider', sanitize_key( $data['sms_provider'] ) );
        }
        if ( isset( $data['sms_api_key'] ) && $data['sms_api_key'] !== '' ) {
            update_option( 'acc_sms_api_key', sanitize_text_field( $data['sms_api_key'] ) );
        }
        if ( isset( $data['sms_sender'] ) ) {
            update_option( 'acc_sms_sender', sanitize_text_field( $data['sms_sender'] ) );
        }
        if ( isset( $data['otp_template'] ) ) {
            update_option( 'acc_sms_otp_template', sanitize_text_field( $data['otp_template'] ) );
        }
        if ( array_key_exists( 'otp_demo', $data ) ) {
            update_option( 'acc_election_otp_demo', ! empty( $data['otp_demo'] ) ? '1' : '0' );
        }
        self::fin_audit( $uid, 'sms_config', 'تنظیمات SMS به‌روز شد' );
        return [ 'success' => true, 'config' => self::sms_config() ];
    }

    /** Import up to SEGMENT_TARGET segments from GIS block_stats */
    public static function import_segments_from_gis( bool $replace = false ): array {
        global $wpdb;
        $pre = $wpdb->prefix;
        $seg_t = self::segments_table();
        $target = self::SEGMENT_TARGET;

        if ( $replace ) {
            $wpdb->query( "DELETE FROM {$seg_t}" );
        }

        $existing = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$seg_t}" );
        if ( $existing >= $target && ! $replace ) {
            return [
                'success'  => true,
                'imported' => 0,
                'total'    => $existing,
                'target'   => $target,
                'message'  => 'پهنه‌ها از قبل import شده',
            ];
        }

        $need = $target - ( $replace ? 0 : $existing );
        $imported = 0;
        $source = 'synthetic';

        $st = self::stations_table();
        $stations = $wpdb->get_results( "SELECT id, district_id, neighborhood, lat, lng FROM {$st}", ARRAY_A ) ?: [];
        $seq = self::next_segment_seq();

        if ( self::gis_tables_exist() ) {
            $bs = $pre . 'gis_block_stats';
            $nb = $pre . 'gis_neighborhoods';
            if ( $wpdb->get_var( "SHOW TABLES LIKE '{$bs}'" ) === $bs ) {
                $blocks = $wpdb->get_results(
                    $wpdb->prepare(
                        "SELECT bs.block_code, bs.neighborhood_id, bs.district_id,
                                bs.total_parcels, bs.cnt_apartment, bs.cnt_building,
                                bs.center_lat, bs.center_lng, n.name AS neighborhood_name
                         FROM {$bs} bs
                         LEFT JOIN {$nb} n ON n.id = bs.neighborhood_id
                         WHERE bs.center_lat IS NOT NULL AND bs.center_lng IS NOT NULL
                         ORDER BY bs.cnt_apartment DESC, bs.total_parcels DESC
                         LIMIT %d",
                        $need
                    ),
                    ARRAY_A
                ) ?: [];

                foreach ( $blocks as $b ) {
                    if ( $seq > $target ) break;
                    $code = sprintf( 'SHZ-%04d', $seq );
                    if ( $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$seg_t} WHERE code=%s", $code ) ) ) {
                        $seq++;
                        continue;
                    }
                    $voters = max( 50, (int) round( ( (int) $b['cnt_apartment'] ) * 2.7 + ( (int) $b['cnt_building'] ) * 8 ) );
                    $sid = self::nearest_station_id( $stations, (float) $b['center_lat'], (float) $b['center_lng'] );
                    $part = ( ( $seq - 1 ) % 3 ) + 1;
                    $wpdb->insert( $seg_t, [
                        'code'                 => $code,
                        'neighborhood'         => $b['neighborhood_name'] ?: ( 'محله #' . $b['neighborhood_id'] ),
                        'part_no'              => $part,
                        'station_id'           => $sid,
                        'voter_count'          => $voters,
                        'lat'                  => $b['center_lat'],
                        'lng'                  => $b['center_lng'],
                        'gis_neighborhood_id'  => (int) ( $b['neighborhood_id'] ?? 0 ) ?: null,
                        'gis_block_code'       => $b['block_code'],
                        'election_district_id' => self::map_gis_district_to_election( (int) ( $b['district_id'] ?? 0 ) ),
                        'import_source'        => 'gis_block',
                        'notes'                => 'پارسل: ' . (int) $b['total_parcels'] . ' — واحد: ' . (int) $b['cnt_apartment'],
                    ] );
                    $imported++;
                    $seq++;
                }
                if ( $imported > 0 ) {
                    $source = 'gis';
                }
            }
        }

        $total = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$seg_t}" );
        if ( $total < $target ) {
            $fill = self::fill_segments_synthetic( $target - $total, $stations, $seq );
            $imported += $fill['imported'];
            if ( $fill['imported'] > 0 && $source !== 'gis' ) {
                $source = 'synthetic';
            } elseif ( $fill['imported'] > 0 ) {
                $source = 'gis+synthetic';
            }
        }

        return [
            'success'  => true,
            'imported' => $imported,
            'total'    => (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$seg_t}" ),
            'target'   => $target,
            'source'   => $source,
        ];
    }

    private static function next_segment_seq(): int {
        global $wpdb;
        $t = self::segments_table();
        $max = (int) $wpdb->get_var(
            "SELECT MAX(CAST(SUBSTRING(code, 5) AS UNSIGNED)) FROM {$t} WHERE code LIKE 'SHZ-%'"
        );
        return max( 1, $max + 1 );
    }

    private static function fill_segments_synthetic( int $count, array $stations, int $start_seq ): array {
        global $wpdb;
        $seg_t = self::segments_table();
        $imported = 0;
        $shiraz = [ 'lat' => 29.5918, 'lng' => 52.5837 ];
        $nb_names = [ 'زند', 'معالی‌آباد', 'درکی', 'ستارخان', 'قصرالدشت', 'گلستان', 'دروازه قرآن', 'عفیف‌آباد' ];
        $seq = $start_seq;

        for ( $i = 0; $i < $count; $i++ ) {
            $idx = $seq - 1;
            $nb = $nb_names[ $idx % count( $nb_names ) ];
            $part = ( $idx % 3 ) + 1;
            $lat = $shiraz['lat'] + ( ( $idx % 37 ) - 18 ) * 0.002;
            $lng = $shiraz['lng'] + ( ( $idx % 41 ) - 20 ) * 0.002;
            $code = sprintf( 'SHZ-%04d', $seq );
            if ( ! $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$seg_t} WHERE code=%s", $code ) ) ) {
                $wpdb->insert( $seg_t, [
                    'code'          => $code,
                    'neighborhood'  => $nb,
                    'part_no'       => $part,
                    'station_id'    => self::nearest_station_id( $stations, $lat, $lng ),
                    'voter_count'   => 180 + ( $idx % 240 ),
                    'lat'           => $lat,
                    'lng'           => $lng,
                    'import_source' => 'synthetic',
                ] );
                $imported++;
            }
            $seq++;
        }

        return [ 'imported' => $imported ];
    }

    private static function nearest_station_id( array $stations, float $lat, float $lng ): ?int {
        $best = null;
        $best_d = PHP_FLOAT_MAX;
        foreach ( $stations as $s ) {
            if ( empty( $s['lat'] ) || empty( $s['lng'] ) ) continue;
            $d = ( $lat - (float) $s['lat'] ) ** 2 + ( $lng - (float) $s['lng'] ) ** 2;
            if ( $d < $best_d ) {
                $best_d = $d;
                $best = (int) $s['id'];
            }
        }
        return $best;
    }

    private static function map_gis_district_to_election( int $gis_district_id ): ?int {
        global $wpdb;
        $dt = self::districts_table();
        if ( ! $gis_district_id || $wpdb->get_var( "SHOW TABLES LIKE '{$dt}'" ) !== $dt ) {
            return null;
        }
        $codes = [ 1 => 'D-1', 2 => 'D-2', 3 => 'D-3', 4 => 'D-4' ];
        $code = $codes[ $gis_district_id ] ?? null;
        if ( ! $code ) {
            $n = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$dt}" );
            $idx = ( ( $gis_district_id - 1 ) % max( 1, $n ) ) + 1;
            return (int) $wpdb->get_var( $wpdb->prepare(
                "SELECT id FROM {$dt} ORDER BY sort_order, id LIMIT 1 OFFSET %d", $idx - 1
            ) ) ?: null;
        }
        return (int) $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$dt} WHERE code=%s", $code ) ) ?: null;
    }

    public static function segment_stats(): array {
        global $wpdb;
        $t = self::segments_table();
        $total = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$t}" );
        $voters = (int) $wpdb->get_var( "SELECT COALESCE(SUM(voter_count),0) FROM {$t}" );
        $from_gis = (int) $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM {$t} WHERE import_source=%s", 'gis_block'
        ) );
        return [
            'total'    => $total,
            'target'   => self::SEGMENT_TARGET,
            'percent'  => round( min( 100, $total / self::SEGMENT_TARGET * 100 ), 1 ),
            'voters'   => $voters,
            'from_gis' => $from_gis,
        ];
    }

    public static function admin_list_segments( array $opts = [] ): array {
        global $wpdb;
        $t = self::segments_table();
        $limit = min( 500, max( 1, (int) ( $opts['limit'] ?? 100 ) ) );
        $offset = max( 0, (int) ( $opts['offset'] ?? 0 ) );
        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT s.*, st.code AS station_code, st.name AS station_name
                 FROM {$t} s LEFT JOIN " . self::stations_table() . " st ON st.id = s.station_id
                 ORDER BY s.code LIMIT %d OFFSET %d",
                $limit, $offset
            ),
            ARRAY_A
        ) ?: [];
        return [
            'stats'    => self::segment_stats(),
            'segments' => array_map( [ __CLASS__, 'fmt_segment' ], $rows ),
        ];
    }

    public static function financial_dashboard(): array {
        global $wpdb;
        $bt = self::budget_table();
        $tt = self::transactions_table();
        $budget = [];
        $tx = [];
        if ( $wpdb->get_var( "SHOW TABLES LIKE '{$bt}'" ) === $bt ) {
            $budget = $wpdb->get_results( "SELECT * FROM {$bt} ORDER BY category, id", ARRAY_A ) ?: [];
        }
        if ( $wpdb->get_var( "SHOW TABLES LIKE '{$tt}'" ) === $tt ) {
            $tx = $wpdb->get_results(
                "SELECT t.*, u.display_name AS created_by_name
                 FROM {$tt} t LEFT JOIN {$wpdb->users} u ON u.ID = t.created_by
                 ORDER BY t.entry_date DESC, t.id DESC LIMIT 100",
                ARRAY_A
            ) ?: [];
        }
        $planned = array_sum( array_column( $budget, 'planned_amount' ) );
        $spent = 0;
        $income = 0;
        foreach ( $tx as $row ) {
            $a = (float) $row['amount'];
            if ( ( $row['direction'] ?? '' ) === 'out' ) {
                $spent += $a;
            } else {
                $income += $a;
            }
        }
        return [
            'categories'    => self::BUDGET_CATEGORIES,
            'budget_lines'  => array_map( function( $b ) {
                $b['category_label'] = self::BUDGET_CATEGORIES[ $b['category'] ?? '' ] ?? ( $b['category'] ?? '' );
                return $b;
            }, $budget ),
            'transactions'  => array_map( function( $t ) {
                $t['category_label'] = self::BUDGET_CATEGORIES[ $t['category'] ?? '' ] ?? ( $t['category'] ?? '' );
                return $t;
            }, $tx ),
            'audit'         => self::list_fin_audit( 30 ),
            'summary'       => [
                'planned'   => $planned,
                'spent'     => $spent,
                'income'    => $income,
                'remaining' => $planned - $spent,
                'balance'   => $income - $spent,
            ],
            'segment_stats' => self::segment_stats(),
        ];
    }

    public static function can_financial( int $uid ): bool {
        return self::can_manage( $uid ) || acl_user_can( $uid, 'election.dept.financial' );
    }

    public static function add_transaction( int $uid, array $data ): array {
        if ( ! self::can_financial( $uid ) ) {
            return [ 'success' => false, 'message' => 'دسترسی مالی ندارید' ];
        }
        global $wpdb;
        $tt = self::transactions_table();
        $dir = in_array( $data['direction'] ?? '', [ 'in', 'out' ], true ) ? $data['direction'] : 'out';
        $amount = max( 0, (float) ( $data['amount'] ?? 0 ) );
        if ( $amount <= 0 ) {
            return [ 'success' => false, 'message' => 'مبلغ نامعتبر' ];
        }
        $wpdb->insert( $tt, [
            'direction'   => $dir,
            'category'    => sanitize_key( $data['category'] ?? 'other' ),
            'amount'      => $amount,
            'description' => sanitize_text_field( $data['description'] ?? '' ),
            'dept_slug'   => sanitize_key( $data['dept_slug'] ?? 'financial' ),
            'phase'       => sanitize_key( $data['phase'] ?? self::current_phase() ),
            'ref_type'    => sanitize_key( $data['ref_type'] ?? '' ) ?: null,
            'ref_id'      => (int) ( $data['ref_id'] ?? 0 ) ?: null,
            'entry_date'  => sanitize_text_field( $data['entry_date'] ?? current_time( 'Y-m-d' ) ),
            'created_by'  => $uid,
            'created_at'  => current_time( 'mysql' ),
        ] );
        $id = (int) $wpdb->insert_id;
        self::fin_audit( $uid, 'transaction_add', "tx#{$id} {$dir} {$amount}" );

        require_once ACC_PATH . 'includes/helpers/acc-portal-accounting.php';
        Acc_Portal_Accounting::ensure_chart( 'election' );
        $gl_post = Acc_Portal_Accounting::post_election_transaction( $id );

        return [
            'success'    => true,
            'id'         => $id,
            'gl_post'    => $gl_post,
            'acc_event_id' => (int) ( $gl_post['event_id'] ?? 0 ),
        ];
    }

    public static function update_budget_line( int $uid, array $data ): array {
        if ( ! self::can_financial( $uid ) ) {
            return [ 'success' => false, 'message' => 'دسترسی ندارید' ];
        }
        global $wpdb;
        $bt = self::budget_table();
        $id = (int) ( $data['id'] ?? 0 );
        if ( $id ) {
            $wpdb->update( $bt, [
                'planned_amount' => max( 0, (float) ( $data['planned_amount'] ?? 0 ) ),
                'notes'          => sanitize_textarea_field( $data['notes'] ?? '' ),
            ], [ 'id' => $id ] );
            self::fin_audit( $uid, 'budget_update', "budget#{$id}" );
            return [ 'success' => true, 'id' => $id ];
        }
        $wpdb->insert( $bt, [
            'category'       => sanitize_key( $data['category'] ?? 'other' ),
            'label'          => sanitize_text_field( $data['label'] ?? '' ),
            'planned_amount' => max( 0, (float) ( $data['planned_amount'] ?? 0 ) ),
            'phase'          => sanitize_key( $data['phase'] ?? 'campaign' ),
            'dept_slug'      => sanitize_key( $data['dept_slug'] ?? 'financial' ),
            'notes'          => sanitize_textarea_field( $data['notes'] ?? '' ),
            'created_at'     => current_time( 'mysql' ),
        ] );
        $new_id = (int) $wpdb->insert_id;
        self::fin_audit( $uid, 'budget_add', "budget#{$new_id}" );
        return [ 'success' => true, 'id' => $new_id ];
    }

    public static function fin_audit( int $uid, string $action, string $detail ): void {
        global $wpdb;
        $t = self::fin_audit_table();
        if ( $wpdb->get_var( "SHOW TABLES LIKE '{$t}'" ) !== $t ) {
            return;
        }
        $wpdb->insert( $t, [
            'user_id'    => $uid,
            'action'     => sanitize_key( $action ),
            'detail'     => sanitize_textarea_field( $detail ),
            'created_at' => current_time( 'mysql' ),
        ] );
    }

    public static function list_fin_audit( int $limit = 50 ): array {
        global $wpdb;
        $t = self::fin_audit_table();
        if ( $wpdb->get_var( "SHOW TABLES LIKE '{$t}'" ) !== $t ) {
            return [];
        }
        return $wpdb->get_results( $wpdb->prepare(
            "SELECT a.*, u.display_name FROM {$t} a
             LEFT JOIN {$wpdb->users} u ON u.ID = a.user_id
             ORDER BY a.id DESC LIMIT %d", $limit
        ), ARRAY_A ) ?: [];
    }

    public static function seed_budget_lines(): int {
        global $wpdb;
        $bt = self::budget_table();
        if ( $wpdb->get_var( "SHOW TABLES LIKE '{$bt}'" ) !== $bt ) {
            return 0;
        }
        $existing = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$bt}" );
        if ( $existing > 0 ) {
            return 0;
        }
        $lines = [
            [ 'media',        'رسانه و آگهی',           8500000000 ],
            [ 'distribution', 'چاپ و توزیع',            4200000000 ],
            [ 'goods',        'اقلام تبلیغاتی',         3100000000 ],
            [ 'field',        'عملیات میدانی',          2800000000 ],
            [ 'staff',        'نیروی انسانی موقت',      5600000000 ],
            [ 'logistics',    'اداری و پشتیبانی',       1900000000 ],
            [ 'tech',         'GIS و سامانه',           1200000000 ],
            [ 'legal',        'حقوقی و نظارت',          800000000 ],
        ];
        foreach ( $lines as [ $cat, $label, $amt ] ) {
            $wpdb->insert( $bt, [
                'category'       => $cat,
                'label'          => $label,
                'planned_amount' => $amt,
                'phase'          => 'campaign',
                'dept_slug'      => 'financial',
                'created_at'     => current_time( 'mysql' ),
            ] );
        }
        return count( $lines );
    }
}
