<?php
/**
 * AuctionForge Legal Agreements
 * Handles storage, versioning, and retrieval of Terms & Conditions,
 * Refund & Return Policy, and Chargeback Waiver documents.
 *
 * @since 3.6.0.0
 */

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

class AF_Legal_Agreements {

    const TYPE_TERMS       = 'terms_conditions';
    const TYPE_REFUND      = 'refund_policy';
    const TYPE_CHARGEBACK  = 'chargeback_waiver';

    const OPTION_REFUND_ENABLED     = 'af_legal_refund_enabled';
    const OPTION_CHARGEBACK_ENABLED = 'af_legal_chargeback_enabled';

    /**
     * Create database tables for legal agreements.
     */
    public static function create_tables() {
        global $wpdb;
        $charset_collate = $wpdb->get_charset_collate();

        $agreements_table = $wpdb->prefix . 'af_legal_agreements';
        $versions_table   = $wpdb->prefix . 'af_legal_agreement_versions';
        $log_table        = $wpdb->prefix . 'af_user_agreement_log';

        $sql_agreements = "CREATE TABLE IF NOT EXISTS $agreements_table (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            agreement_type varchar(50) NOT NULL,
            content longtext NOT NULL,
            version int unsigned NOT NULL DEFAULT 1,
            updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_by bigint(20) unsigned NOT NULL DEFAULT 0,
            PRIMARY KEY (id),
            UNIQUE KEY idx_agreement_type (agreement_type)
        ) $charset_collate;";

        $sql_versions = "CREATE TABLE IF NOT EXISTS $versions_table (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            agreement_type varchar(50) NOT NULL,
            content longtext NOT NULL,
            version int unsigned NOT NULL,
            created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
            created_by bigint(20) unsigned NOT NULL DEFAULT 0,
            PRIMARY KEY (id),
            KEY idx_type_version (agreement_type, version)
        ) $charset_collate;";

        $sql_log = "CREATE TABLE IF NOT EXISTS $log_table (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            agreement_type varchar(50) NOT NULL,
            version_agreed int unsigned NOT NULL,
            user_email varchar(255) NOT NULL DEFAULT '',
            visitor_hash varchar(64) NOT NULL DEFAULT '',
            ip_address varchar(45) NOT NULL DEFAULT '',
            user_agent varchar(500) DEFAULT '',
            auction_id varchar(100) DEFAULT '',
            created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY idx_user_email (user_email),
            KEY idx_type_version (agreement_type, version_agreed),
            KEY idx_auction_id (auction_id),
            KEY idx_created_at (created_at)
        ) $charset_collate;";

        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql_agreements);
        dbDelta($sql_versions);
        dbDelta($sql_log);
    }

    /**
     * Get the current active agreement for a given type.
     *
     * @param string $type One of the TYPE_* constants.
     * @return object|null {content, version, updated_at, updated_by}
     */
    public static function get_agreement($type) {
        global $wpdb;
        $table = $wpdb->prefix . 'af_legal_agreements';
        return $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $table WHERE agreement_type = %s",
            $type
        ));
    }

    /**
     * Get all current agreements (keyed by type).
     *
     * @return array Associative array of type => row object.
     */
    public static function get_all_agreements() {
        global $wpdb;
        $table = $wpdb->prefix . 'af_legal_agreements';
        $rows = $wpdb->get_results("SELECT * FROM $table");
        $agreements = array();
        foreach ($rows as $row) {
            $agreements[$row->agreement_type] = $row;
        }
        return $agreements;
    }

    /**
     * Save or update an agreement. Auto-increments version on content change.
     *
     * @param string $type    Agreement type.
     * @param string $content HTML content.
     * @return int New version number.
     */
    public static function save_agreement($type, $content) {
        global $wpdb;
        $table    = $wpdb->prefix . 'af_legal_agreements';
        $v_table  = $wpdb->prefix . 'af_legal_agreement_versions';
        $user_id  = get_current_user_id();
        $now      = current_time('mysql');

        $existing = self::get_agreement($type);

        if ($existing) {
            // Check if content actually changed
            if (trim($existing->content) === trim($content)) {
                return intval($existing->version);
            }

            $new_version = intval($existing->version) + 1;

            // Update the current record
            $wpdb->update($table, array(
                'content'    => $content,
                'version'    => $new_version,
                'updated_at' => $now,
                'updated_by' => $user_id,
            ), array('agreement_type' => $type), array('%s', '%d', '%s', '%d'), array('%s'));

            // Archive the previous version
            $wpdb->insert($v_table, array(
                'agreement_type' => $type,
                'content'        => $existing->content,
                'version'        => $existing->version,
                'created_at'     => $existing->updated_at,
                'created_by'     => $existing->updated_by,
            ), array('%s', '%s', '%d', '%s', '%d'));

            return $new_version;
        } else {
            // Insert new
            $wpdb->insert($table, array(
                'agreement_type' => $type,
                'content'        => $content,
                'version'        => 1,
                'updated_at'     => $now,
                'updated_by'     => $user_id,
            ), array('%s', '%s', '%d', '%s', '%d'));

            return 1;
        }
    }

    /**
     * Get version history for a given agreement type.
     *
     * @param string $type Agreement type.
     * @param int    $limit Max versions to return.
     * @return array
     */
    public static function get_version_history($type, $limit = 20) {
        global $wpdb;
        $table = $wpdb->prefix . 'af_legal_agreement_versions';
        return $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM $table WHERE agreement_type = %s ORDER BY version DESC LIMIT %d",
            $type, $limit
        ));
    }

    /**
     * Get the content of a specific version of an agreement.
     * Checks the current active record first, then the version archive.
     *
     * @param string $type    Agreement type.
     * @param int    $version Version number.
     * @return object|null {content, version, agreement_type, created_at/updated_at}
     */
    public static function get_version_content($type, $version) {
        global $wpdb;

        // Check if this is the current active version
        $current = self::get_agreement($type);
        if ($current && intval($current->version) === intval($version)) {
            return $current;
        }

        // Look in the version history archive
        $table = $wpdb->prefix . 'af_legal_agreement_versions';
        return $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $table WHERE agreement_type = %s AND version = %d",
            $type, $version
        ));
    }

    /**
     * Log a user's agreement acceptance.
     *
     * @param array $data {agreement_type, version_agreed, user_email, visitor_hash, ip_address, user_agent, auction_id}
     * @return int|false Insert ID or false on failure.
     */
    public static function log_agreement($data) {
        global $wpdb;
        $table = $wpdb->prefix . 'af_user_agreement_log';
        $result = $wpdb->insert($table, array(
            'agreement_type'  => sanitize_text_field($data['agreement_type']),
            'version_agreed'  => intval($data['version_agreed']),
            'user_email'      => sanitize_email($data['user_email']),
            'visitor_hash'    => sanitize_text_field($data['visitor_hash']),
            'ip_address'      => sanitize_text_field($data['ip_address']),
            'user_agent'      => sanitize_text_field($data['user_agent']),
            'auction_id'      => sanitize_text_field($data['auction_id']),
            'created_at'      => current_time('mysql'),
        ), array('%s', '%d', '%s', '%s', '%s', '%s', '%s', '%s'));

        return $result ? $wpdb->insert_id : false;
    }

    /**
     * Check whether a specific agreement type is enabled.
     *
     * @param string $type Agreement type.
     * @return bool
     */
    public static function is_enabled($type) {
        switch ($type) {
            case self::TYPE_TERMS:
                return true; // Always enabled
            case self::TYPE_REFUND:
                $settings = get_option('bidspirit_settings', array());
                return isset($settings['enable_refund_policy']) && $settings['enable_refund_policy'] === 'on';
            case self::TYPE_CHARGEBACK:
                $settings = get_option('bidspirit_settings', array());
                return isset($settings['enable_chargeback_waiver']) && $settings['enable_chargeback_waiver'] === 'on';
            default:
                return false;
        }
    }

    /**
     * Get all enabled agreements with their current content/version.
     * Used by the frontend to display in the approval modal.
     *
     * @return array Array of {type, content, version} for enabled agreements.
     */
    public static function get_enabled_agreements() {
        $types = array(self::TYPE_TERMS, self::TYPE_REFUND, self::TYPE_CHARGEBACK);
        $result = array();

        foreach ($types as $type) {
            if (!self::is_enabled($type)) continue;

            $agreement = self::get_agreement($type);
            if ($agreement && !empty(trim($agreement->content))) {
                $result[] = array(
                    'type'    => $type,
                    'content' => $agreement->content,
                    'version' => intval($agreement->version),
                );
            }
        }

        return $result;
    }

    /**
     * Get human-readable label for an agreement type.
     *
     * @param string $type Agreement type.
     * @return string
     */
    public static function get_type_label($type) {
        switch ($type) {
            case self::TYPE_TERMS:
                return __('Terms & Conditions', 'auctionforge');
            case self::TYPE_REFUND:
                return __('Refund & Return Policy', 'auctionforge');
            case self::TYPE_CHARGEBACK:
                return __('Chargeback Waiver', 'auctionforge');
            default:
                return $type;
        }
    }

    /**
     * Get agreement logs for admin review.
     *
     * @param array $filters Optional filters: user_email, agreement_type, auction_id
     * @param int   $per_page
     * @param int   $page
     * @return array {results, total, page, pages}
     */
    public static function get_agreement_logs($filters = array(), $per_page = 50, $page = 1) {
        global $wpdb;
        $table = $wpdb->prefix . 'af_user_agreement_log';
        $offset = ($page - 1) * $per_page;

        $where = array();
        $params = array();

        if (!empty($filters['user_email'])) {
            $where[] = "user_email LIKE %s";
            $params[] = '%' . $wpdb->esc_like($filters['user_email']) . '%';
        }
        if (!empty($filters['agreement_type'])) {
            $where[] = "agreement_type = %s";
            $params[] = $filters['agreement_type'];
        }
        if (!empty($filters['auction_id'])) {
            $where[] = "auction_id = %s";
            $params[] = $filters['auction_id'];
        }

        $where_sql = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';

        $count_query = "SELECT COUNT(*) FROM $table $where_sql";
        $total = !empty($params)
            ? $wpdb->get_var($wpdb->prepare($count_query, $params))
            : $wpdb->get_var($count_query);

        $all_params = array_merge($params, array($per_page, $offset));
        $results = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM $table $where_sql ORDER BY created_at DESC LIMIT %d OFFSET %d",
            $all_params
        ), ARRAY_A);

        return array(
            'results' => $results,
            'total'   => intval($total),
            'page'    => $page,
            'pages'   => ceil(intval($total) / max($per_page, 1)),
        );
    }

    /**
     * Seed default placeholder content for all agreement types.
     */
    public static function seed_defaults() {
        $defaults = array(
            self::TYPE_TERMS => '<h3>Terms &amp; Conditions of Sale</h3>
<p>Welcome to our auction house. By registering to bid in any of our auctions, you agree to the following terms and conditions:</p>

<h4>1. Registration &amp; Eligibility</h4>
<p>All bidders must register and be approved before placing bids. Registration requires valid identification, contact information, and, where applicable, a valid payment method on file. The auction house reserves the right to refuse registration at its sole discretion.</p>

<h4>2. Bidding</h4>
<p>All bids are binding and irrevocable. By placing a bid, you enter into a legally binding contract to purchase the lot at the bid price plus any applicable buyer&rsquo;s premium, taxes, and fees. The auction house reserves the right to reject any bid at its sole discretion.</p>

<h4>3. Buyer&rsquo;s Premium</h4>
<p>A buyer&rsquo;s premium will be added to the hammer price of each lot. The applicable percentage will be stated in the auction listing. Sales tax and/or VAT may also apply.</p>

<h4>4. Payment</h4>
<p>Payment is due within the timeframe specified in the auction terms. Accepted payment methods include credit card, wire transfer, and other methods as specified. Late payment may result in additional charges and loss of bidding privileges.</p>

<h4>5. Collection &amp; Shipping</h4>
<p>Buyers are responsible for collecting or arranging shipping of purchased lots within the specified timeframe. Storage fees may apply after the designated collection period.</p>

<h4>6. Condition of Lots</h4>
<p>All lots are sold &ldquo;as is&rdquo; and &ldquo;where is.&rdquo; The auction house makes no warranties or representations regarding the condition, authenticity, or value of any lot. Prospective buyers are encouraged to inspect lots prior to bidding.</p>

<h4>7. Limitation of Liability</h4>
<p>The auction house shall not be liable for any indirect, incidental, or consequential damages arising from the purchase or use of any lot. Total liability shall not exceed the purchase price of the lot in question.</p>

<p><em>These terms are subject to change. The version in effect at the time of your registration for a specific auction applies to all transactions within that auction.</em></p>',

            self::TYPE_REFUND => '<h3>Refund &amp; Return Policy</h3>
<p>Please read our refund and return policy carefully before bidding.</p>

<h4>General Policy</h4>
<p>All sales are final. Due to the nature of auction sales, items are generally sold &ldquo;as is&rdquo; and are not eligible for return or refund. By placing a bid, you acknowledge and accept this policy.</p>

<h4>Exceptions</h4>
<p>Refunds or returns may be considered in the following limited circumstances:</p>
<ul>
<li>The lot received is materially different from the catalog description</li>
<li>The lot has a significant undisclosed defect that was not visible during preview</li>
<li>Authenticity issues where the lot is proven to be counterfeit</li>
</ul>

<h4>Process</h4>
<p>Any refund request must be submitted in writing within 48 hours of receipt of the lot, accompanied by photographic evidence. The auction house will review each case individually and reserves the right to make the final determination.</p>

<p><em>This policy is governed by the laws of the jurisdiction in which the auction house operates.</em></p>',

            self::TYPE_CHARGEBACK => '<h3>Chargeback Waiver</h3>
<p>By registering to bid and providing your payment information, you acknowledge and agree to the following:</p>

<h4>Waiver of Chargeback Rights</h4>
<p>You agree that you will not initiate a chargeback, dispute, or reversal of any charges made to your payment method in connection with purchases made at auction. All bids are final and binding. If you have a concern about a purchase, you agree to contact the auction house directly to resolve the matter before taking any action with your payment provider.</p>

<h4>Consequences of Chargeback</h4>
<p>If a chargeback is initiated in violation of this waiver:</p>
<ul>
<li>Your account will be immediately suspended</li>
<li>You will be liable for all costs associated with the chargeback, including administrative fees</li>
<li>The auction house reserves the right to pursue legal action to recover the full amount plus damages</li>
<li>You will be permanently banned from participating in future auctions</li>
</ul>

<h4>Dispute Resolution</h4>
<p>In the event of any dispute, you agree to first attempt resolution directly with the auction house. If a resolution cannot be reached, both parties agree to submit to binding arbitration in accordance with applicable laws.</p>

<p><em>This waiver is a material condition of your registration and bidding privileges.</em></p>',
        );

        foreach ($defaults as $type => $content) {
            $existing = self::get_agreement($type);
            if (!$existing) {
                global $wpdb;
                $table = $wpdb->prefix . 'af_legal_agreements';
                $wpdb->insert($table, array(
                    'agreement_type' => $type,
                    'content'        => $content,
                    'version'        => 1,
                    'updated_at'     => current_time('mysql'),
                    'updated_by'     => get_current_user_id(),
                ), array('%s', '%s', '%d', '%s', '%d'));
            }
        }
    }
}
