<?php

if (!defined('ABSPATH')) { exit; }

class AIEO_Fulfillment_Closings {

    const PDF_DIR_REL = 'aieo-dmm/fulfillment/closings';

    private static function table() {
        global $wpdb;
        return $wpdb->prefix . 'aieo_dmm_fulfillment_closings';
    }

    private static function vouchers_table() {
        global $wpdb;
        return $wpdb->prefix . 'aieo_dmm_fulfillment_vouchers';
    }

    
    public static function close_and_log($driver, $mode = 'now', $from_date = null, $to_date = null) {
        $carrier = method_exists($driver, 'code') ? (string) $driver->code() : '';
        if ($carrier === '') {
            return array('ok' => false, 'error' => 'driver has no code()');
        }

        
        
        
        
        $pending_rows = self::list_pending_for_close($carrier, $mode, $from_date, $to_date);

        
        
        
        
        
        
        
        
        
        $has_api_close = method_exists($driver, 'has_capability') && $driver->has_capability('close_pending');

        if (($mode === 'by_date') && ($from_date === null || $to_date === null)) {
            return array('ok' => false, 'error' => 'from_date and to_date required for by_date mode');
        }

        
        
        
        
        
        if ($mode === 'cron' && !$has_api_close && empty($pending_rows)) {
            return array('ok' => true, 'local' => true, 'skipped' => true, 'voucher_count' => 0);
        }

        
        $carrier_pdf   = null;
        $driver_result = array('ok' => true);
        if ($has_api_close) {
            try {
                if ($mode === 'by_date') {
                    $driver_result = $driver->close_pending_by_date($from_date . 'T00:00:00', $to_date . 'T23:59:59');
                } else {
                    $driver_result = $driver->close_pending();
                }
            } catch (\Throwable $e) {
                $driver_result = array('ok' => false, 'error' => 'throw: ' . $e->getMessage());
            }
        } elseif (method_exists($driver, 'has_capability') && $driver->has_capability('manifest') && method_exists($driver, 'manifest')) {
            
            
            
            $mdate = ($mode === 'by_date' && $from_date) ? (string) $from_date : current_time('Y-m-d');
            try {
                $driver_result = $driver->manifest($mdate);
                if (!empty($driver_result['ok']) && !empty($driver_result['pdf'])) {
                    $carrier_pdf = (string) $driver_result['pdf'];
                }
            } catch (\Throwable $e) {
                $driver_result = array('ok' => false, 'error' => 'throw: ' . $e->getMessage());
            }
        } else {
            
            $driver_result = array('ok' => true, 'local' => true);
        }

        $status = !empty($driver_result['ok']) ? 'ok' : 'err';
        $error  = !empty($driver_result['ok']) ? '' : (string) ($driver_result['error'] ?? 'unknown');

        
        
        
        if ($status === 'ok' && !empty($pending_rows)) {
            self::mark_vouchers_closed(array_column($pending_rows, 'id'));
        }

        
        
        
        $manifest_rows = array();
        foreach ($pending_rows as $row) {
            $manifest_rows[] = self::denormalise_row($row);
        }

        $insert = array(
            'carrier'         => $carrier,
            'closing_mode'    => sanitize_key((string) $mode),
            'from_date'       => $from_date !== null ? sanitize_text_field((string) $from_date) : null,
            'to_date'         => $to_date   !== null ? sanitize_text_field((string) $to_date)   : null,
            'voucher_count'   => count($manifest_rows),
            'voucher_ids'     => wp_json_encode(array_column($pending_rows, 'id')),
            'manifest_rows'   => wp_json_encode($manifest_rows),
            'status'          => $status,
            'error_message'   => mb_substr($error, 0, 500),
            'closed_by'       => $mode === 'cron' ? null : (get_current_user_id() ?: null),
            'pdf_path'        => '',
            'closed_at'       => current_time('mysql', true),
            'created_at'      => current_time('mysql', true),
        );
        global $wpdb;
        $r = $wpdb->insert(self::table(), $insert);
        if ($r === false) {
            return array('ok' => $status === 'ok', 'error' => 'closings insert failed: ' . $wpdb->last_error);
        }
        $new_id = (int) $wpdb->insert_id;

        
        
        
        if ($carrier_pdf !== null && $status === 'ok') {
            self::store_carrier_pdf($new_id, $carrier, $carrier_pdf);
        }

        return array(
            'ok'            => $status === 'ok',
            'id'            => $new_id,
            'voucher_count' => count($manifest_rows),
            'error'         => $error,
        );
    }

    
    private static function store_carrier_pdf($closing_id, $carrier, $pdf) {
        if ($pdf === '' || substr((string) $pdf, 0, 4) !== '%PDF') { return; }
        $upload  = wp_upload_dir(null, false);
        $rel_dir = self::PDF_DIR_REL . '/' . gmdate('Y/m');
        $abs_dir = trailingslashit($upload['basedir']) . $rel_dir;
        if (!wp_mkdir_p($abs_dir)) { return; }
        $filename = sanitize_file_name(sprintf('closing-%s-%d.pdf', $carrier, (int) $closing_id));
        if (file_put_contents($abs_dir . '/' . $filename, $pdf) === false) { return; }
        global $wpdb;
        $wpdb->update(self::table(), array('pdf_path' => $rel_dir . '/' . $filename), array('id' => (int) $closing_id));
    }

    
    public static function get_last_for_carrier($carrier, $include_failed = false) {
        global $wpdb;
        $carrier = sanitize_key((string) $carrier);
        if ($carrier === '') { return null; }
        $sql = $include_failed
            ? "SELECT * FROM " . self::table() . " WHERE carrier = %s ORDER BY id DESC LIMIT 1"
            : "SELECT * FROM " . self::table() . " WHERE carrier = %s AND status = 'ok' ORDER BY id DESC LIMIT 1";
        $row = $wpdb->get_row($wpdb->prepare($sql, $carrier), ARRAY_A);
        return is_array($row) ? self::hydrate($row) : null;
    }

    public static function get($id) {
        global $wpdb;
        $id = (int) $id;
        if ($id <= 0) { return null; }
        $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM " . self::table() . " WHERE id = %d", $id), ARRAY_A);
        return is_array($row) ? self::hydrate($row) : null;
    }

    
    public static function ensure_pdf($closing_id) {
        $row = self::get($closing_id);
        if (!$row) { return new WP_Error('not_found', __('Closing not found.', 'ai-eshop-optimizer')); }

        
        if (!empty($row['pdf_path'])) {
            $abs = self::abs_from_rel((string) $row['pdf_path']);
            if (file_exists($abs)) { return (string) $row['pdf_path']; }
        }

        $html = self::render_html($row);
        if (is_wp_error($html)) { return $html; }
        $pdf  = AIEO_Fulfillment_PDF_Engine::render($html, array('paper' => 'a4', 'orientation' => 'portrait'));
        if (is_wp_error($pdf)) { return $pdf; }

        
        $upload   = wp_upload_dir(null, false);
        $rel_dir  = self::PDF_DIR_REL . '/' . gmdate('Y/m');
        $abs_dir  = trailingslashit($upload['basedir']) . $rel_dir;
        if (!wp_mkdir_p($abs_dir)) {
            return new WP_Error('mkdir_failed', 'could not create ' . $abs_dir);
        }
        $filename = sanitize_file_name(sprintf('closing-%s-%d.pdf', $row['carrier'], (int) $row['id']));
        $abs_path = $abs_dir . '/' . $filename;
        if (file_put_contents($abs_path, $pdf) === false) {
            return new WP_Error('write_failed', 'could not write ' . $abs_path);
        }
        $rel_path = $rel_dir . '/' . $filename;

        global $wpdb;
        $wpdb->update(self::table(), array('pdf_path' => $rel_path), array('id' => (int) $row['id']));
        return $rel_path;
    }

    
    public static function pdf_url($rel_path) {
        $upload = wp_upload_dir(null, false);
        return trailingslashit($upload['baseurl']) . ltrim((string) $rel_path, '/');
    }

    public static function abs_from_rel($rel_path) {
        $upload = wp_upload_dir(null, false);
        return trailingslashit($upload['basedir']) . ltrim((string) $rel_path, '/');
    }

    

    
    private static function list_pending_for_close($carrier, $mode, $from_date, $to_date) {
        global $wpdb;
        $t = self::vouchers_table();
        if ($mode === 'by_date' && $from_date && $to_date) {
            $rows = $wpdb->get_results($wpdb->prepare(
                "SELECT * FROM {$t}
                 WHERE carrier = %s
                   AND closed_at IS NULL
                   AND status = 'created'
                   AND created_at >= %s
                   AND created_at <= %s
                 ORDER BY id ASC",
                $carrier,
                $from_date . ' 00:00:00',
                $to_date . ' 23:59:59'
            ), ARRAY_A);
        } else {
            $rows = $wpdb->get_results($wpdb->prepare(
                "SELECT * FROM {$t}
                 WHERE carrier = %s
                   AND closed_at IS NULL
                   AND status = 'created'
                 ORDER BY id ASC",
                $carrier
            ), ARRAY_A);
        }
        return is_array($rows) ? $rows : array();
    }

    private static function mark_vouchers_closed(array $ids) {
        global $wpdb;
        $ids = array_filter(array_map('intval', $ids));
        if (empty($ids)) { return; }
        $placeholders = implode(',', array_fill(0, count($ids), '%d'));
        $sql = "UPDATE " . self::vouchers_table() . " SET closed_at = %s WHERE id IN ({$placeholders})";
        $params = array_merge(array(current_time('mysql', true)), $ids);
        $wpdb->query($wpdb->prepare($sql, $params));
    }

    
    private static function denormalise_row(array $voucher) {
        $consignee = (string) ($voucher['consignee'] ?? '');
        $address   = '';
        $city      = '';
        $reference = (string) ($voucher['reference_1'] ?? '');
        if (function_exists('wc_get_order') && !empty($voucher['order_id'])) {
            $order = wc_get_order((int) $voucher['order_id']);
            if ($order) {
                if ($consignee === '') {
                    $consignee = trim($order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name())
                              ?: trim($order->get_billing_first_name() . ' ' . $order->get_billing_last_name());
                }
                $address = trim($order->get_shipping_address_1() . ' ' . $order->get_shipping_address_2())
                        ?: trim($order->get_billing_address_1()  . ' ' . $order->get_billing_address_2());
                $city = $order->get_shipping_city() ?: $order->get_billing_city();
                if ($reference === '') { $reference = (string) $order->get_order_number(); }
            }
        }
        return array(
            'voucher_id'   => (int) $voucher['id'],
            'voucher_no'   => (string) ($voucher['voucher_no'] ?? ''),
            'consignee'    => $consignee,
            'address'      => $address,
            'city'         => $city,
            'reference'    => $reference,
            'weight'       => isset($voucher['weight']) ? (float) $voucher['weight'] : null,
            'cod_amount'   => isset($voucher['cod_amount']) ? (float) $voucher['cod_amount'] : 0.0,
            'services_csv' => (string) ($voucher['services_csv'] ?? ''),
            'notes'        => '',
        );
    }

    private static function hydrate(array $row) {
        $row['voucher_ids']   = self::json_decode_safe($row['voucher_ids']   ?? '');
        $row['manifest_rows'] = self::json_decode_safe($row['manifest_rows'] ?? '');
        return $row;
    }

    private static function json_decode_safe($s) {
        if (!is_string($s) || $s === '') { return array(); }
        $v = json_decode($s, true);
        return is_array($v) ? $v : array();
    }

    
    private static function render_html(array $row) {
        $carrier        = (string) $row['carrier'];
        $courier_cfg    = (array) AIEO_Fulfillment_Settings::get('courier_' . $carrier, array());
        $customer_code  = isset($courier_cfg['customer_code']) ? (string) $courier_cfg['customer_code'] : '';
        $footer_text    = (string) AIEO_Fulfillment_Settings::get('manifest_footer', '');
        if ($footer_text === '') {
            
            $g           = (array) AIEO_Fulfillment_Settings::get('general', array());
            $shop_name   = isset($g['shop_name']) ? (string) $g['shop_name'] : (string) get_bloginfo('name');
            $footer_text = 'Ο ΑΠΟΣΤΟΛΕΑΣ - ' . $shop_name;
        }
        $label_map      = array('now' => 'Σε Κλείσιμο', 'by_date' => 'Κλείσιμο εύρους ημερομηνιών', 'cron' => 'Αυτόματο');
        $mode_label     = isset($label_map[$row['closing_mode']]) ? $label_map[$row['closing_mode']] : (string) $row['closing_mode'];
        $from_label     = !empty($row['from_date']) ? gmdate('j/n/Y', strtotime((string) $row['from_date'])) : gmdate('j/n/Y', strtotime((string) $row['closed_at']));
        $to_label       = !empty($row['to_date'])   ? gmdate('j/n/Y', strtotime((string) $row['to_date']))   : $from_label;
        $manifest_rows  = is_array($row['manifest_rows']) ? $row['manifest_rows'] : array();
        $voucher_count  = (int) $row['voucher_count'];

        $tpl = AIEO_DIR . 'templates/fulfillment/closing-manifest.php';
        if (!file_exists($tpl)) {
            return new WP_Error('template_missing', 'Manifest template not found at ' . $tpl);
        }
        ob_start();
        $data = array(
            'carrier'        => $carrier,
            'customer_code'  => $customer_code,
            'footer_text'    => $footer_text,
            'mode_label'     => $mode_label,
            'from_label'     => $from_label,
            'to_label'       => $to_label,
            'voucher_count'  => $voucher_count,
            'manifest_rows'  => $manifest_rows,
        );
        
        extract($data, EXTR_SKIP);
        include $tpl;
        return (string) ob_get_clean();
    }
}
