<?php

class ChainPay_Subscription {
    
    private $table_name;
    private $license_table;
    private $plans_table;
    
    public function __construct() {
        global $wpdb;
        $this->table_name = $wpdb->prefix . 'chainpay_subscriptions';
        $this->license_table = $wpdb->prefix . 'chainpay_licenses';
        $this->plans_table = $wpdb->prefix . 'chainpay_plans';
    }
    
    public function create($data) {
        global $wpdb;
        
        $defaults = array(
            'subscription_id' => $this->generate_subscription_id(),
            'user_id' => null,
            'user_email' => '',
            'license_id' => null,
            'plan_id' => null,
            'status' => 'active',
            'billing_cycle' => 'monthly',
            'amount' => 0.00,
            'currency' => 'USD',
            'start_date' => current_time('mysql'),
            'next_billing_date' => $this->calculate_next_billing_date('monthly'),
            'payment_method' => '',
            'payment_id' => '',
            'metadata' => ''
        );
        
        $data = wp_parse_args($data, $defaults);
        
        $result = $wpdb->insert(
            $this->table_name,
            $data,
            array('%s', '%d', '%s', '%d', '%d', '%s', '%s', '%f', '%s', '%s', '%s', '%s', '%s', '%s')
        );
        
        if ($result === false) {
            return new WP_Error('db_error', 'Failed to create subscription');
        }
        
        return $wpdb->insert_id;
    }
    
    private function generate_subscription_id() {
        return 'SUB-' . strtoupper(wp_generate_uuid4());
    }
    
    private function calculate_next_billing_date($billing_cycle) {
        $interval = $this->get_billing_interval($billing_cycle);
        return date('Y-m-d H:i:s', strtotime("+{$interval}"));
    }
    
    private function get_billing_interval($billing_cycle) {
        $intervals = array(
            'daily' => '1 day',
            'weekly' => '1 week',
            'monthly' => '1 month',
            'quarterly' => '3 months',
            'yearly' => '1 year'
        );
        return $intervals[$billing_cycle] ?? '1 month';
    }
    
    public function get($subscription_id) {
        global $wpdb;
        return $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$this->table_name} WHERE id = %d",
            $subscription_id
        ));
    }
    
    public function get_by_sub_id($subscription_id) {
        global $wpdb;
        return $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$this->table_name} WHERE subscription_id = %s",
            $subscription_id
        ));
    }
    
    public function update($subscription_id, $data) {
        global $wpdb;
        return $wpdb->update(
            $this->table_name,
            $data,
            array('id' => $subscription_id),
            $this->get_format($data),
            array('%d')
        );
    }
    
    public function cancel($subscription_id, $immediately = false) {
        $subscription = $this->get($subscription_id);
        
        if (!$subscription) {
            return new WP_Error('not_found', 'Subscription not found');
        }
        
        $update_data = array(
            'status' => $immediately ? 'canceled' : 'pending_cancellation',
            'canceled_at' => current_time('mysql')
        );
        
        if ($immediately) {
            $update_data['next_billing_date'] = null;
        }
        
        $this->update($subscription_id, $update_data);
        
        return true;
    }
    
    public function renew($subscription_id) {
        $subscription = $this->get($subscription_id);
        
        if (!$subscription) {
            return new WP_Error('not_found', 'Subscription not found');
        }
        
        if ($subscription->status === 'canceled') {
            return new WP_Error('canceled', 'Cannot renew a canceled subscription');
        }
        
        $next_billing = $this->calculate_next_billing_date($subscription->billing_cycle);
        
        $this->update($subscription_id, array(
            'status' => 'active',
            'start_date' => current_time('mysql'),
            'next_billing_date' => $next_billing,
            'canceled_at' => null
        ));
        
        return true;
    }
    
    public function process_recurring_billing() {
        global $wpdb;
        
        $now = current_time('mysql');
        
        $subscriptions = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$this->table_name} 
             WHERE status = 'active' 
             AND next_billing_date <= %s",
            $now
        ));
        
        $results = array();
        
        foreach ($subscriptions as $subscription) {
            $result = $this->process_single_subscription($subscription);
            $results[$subscription->id] = $result;
        }
        
        return $results;
    }
    
    private function process_single_subscription($subscription) {
        do_action('chainpay_before_subscription_renewal', $subscription);
        
        $renewed = $this->renew($subscription->id);
        
        if (is_wp_error($renewed)) {
            return $renewed;
        }
        
        do_action('chainpay_after_subscription_renewal', $subscription);
        
        return true;
    }
    
    public function get_all($args = array()) {
        global $wpdb;
        
        $defaults = array(
            'status' => '',
            'plan_id' => null,
            'user_id' => null,
            'orderby' => 'created_at',
            'order' => 'DESC',
            'limit' => 20,
            'offset' => 0
        );
        
        $args = wp_parse_args($args, $defaults);
        
        $where = array('1=1');
        $values = array();
        
        if (!empty($args['status'])) {
            $where[] = 's.status = %s';
            $values[] = $args['status'];
        }
        
        if ($args['plan_id']) {
            $where[] = 's.plan_id = %d';
            $values[] = $args['plan_id'];
        }
        
        if ($args['user_id']) {
            $where[] = 's.user_id = %d';
            $values[] = $args['user_id'];
        }
        
        $where_clause = implode(' AND ', $where);
        $orderby = sanitize_sql_orderby('s.' . $args['orderby'] . ' ' . $args['order']);
        
        $sql = "SELECT s.*, p.name as plan_name, p.slug as plan_slug 
                FROM {$this->table_name} s 
                LEFT JOIN {$this->plans_table} p ON s.plan_id = p.id 
                WHERE {$where_clause} 
                ORDER BY {$orderby} 
                LIMIT %d OFFSET %d";
        $values[] = $args['limit'];
        $values[] = $args['offset'];
        
        return $wpdb->get_results($wpdb->prepare($sql, $values));
    }
    
    public function get_user_subscriptions($user_id) {
        global $wpdb;
        return $wpdb->get_results($wpdb->prepare(
            "SELECT s.*, p.name as plan_name, p.slug as plan_slug 
             FROM {$this->table_name} s 
             LEFT JOIN {$this->plans_table} p ON s.plan_id = p.id 
             WHERE s.user_id = %d 
             ORDER BY s.created_at DESC",
            $user_id
        ));
    }
    
    public function count($args = array()) {
        global $wpdb;
        
        $defaults = array('status' => '');
        $args = wp_parse_args($args, $defaults);
        
        $where = '1=1';
        $values = array();
        
        if (!empty($args['status'])) {
            $where .= ' AND status = %s';
            $values[] = $args['status'];
        }
        
        $sql = "SELECT COUNT(*) FROM {$this->table_name} WHERE {$where}";
        
        if (!empty($values)) {
            return $wpdb->get_var($wpdb->prepare($sql, $values));
        }
        
        return $wpdb->get_var($sql);
    }
    
    private function get_format($data) {
        $format = array();
        foreach ($data as $key => $value) {
            if (is_int($value)) {
                $format[] = '%d';
            } elseif (is_float($value)) {
                $format[] = '%f';
            } else {
                $format[] = '%s';
            }
        }
        return $format;
    }
}
