<?php
/**
 * Client Repository Class
 *
 * @package Easy_Invoice
 * @subpackage Repositories
 */

namespace EasyInvoice\Repositories;

use EasyInvoice\Constants\ClientFields;
use EasyInvoice\Interfaces\ClientRepositoryInterface;
use EasyInvoice\Models\Client;
use WP_User;
use WP_User_Query;
use WP_Query;

/**
 * ClientRepository Class
 * 
 * Handles data access for client objects using WordPress users.
 */
class ClientRepository implements ClientRepositoryInterface {
    
    /**
     * Find a client by ID
     *
     * @param int $id The client ID
     * @return Client|null The client model or null if not found
     */
    public function find($id) {
        // Regular client (WordPress user)
        $user = get_user_by('id', $id);
        
        if (!$user) {
            return null;
        }
        
        return $this->createClientFromUser($user);
    }
    
    /**
     * Get all clients
     *
     * @param array $args Optional arguments to filter the results
     * @return array Array of Client models
     */
    public function all($args = []) {
        $clients = [];
        
        // Get all users to include administrators and other users
        $user_query = new WP_User_Query([
            'number' => -1,
            'orderby' => 'ID',
            'order' => 'DESC',
            'role__not_in' => ['Administrator'], // exclude admins
        ]);
        
        foreach ($user_query->get_results() as $user) {
            $client = $this->createClientFromUser($user);
            if ($client) {
                $clients[] = $client;
            }
        }
        
        return $clients;
    }
    
    /**
     * Create a new client
     *
     * @param array $data The client data
     * @return Client The created client model
     */
    public function create($data) {
        // Create regular client as WordPress user
        return $this->createRegularClient($data);
    }
    
    /**
     * Create a regular client (WordPress user)
     *
     * @param array $data The client data
     * @return Client|null The created client model
     */
    protected function createRegularClient($data) {
        // Create a new user if email is provided
        if (empty($data[ClientFields::EMAIL])) {
            return null;
        }
       
        $user_data = [
            'user_email' => $data[ClientFields::EMAIL],
            'user_login' => $data[ClientFields::USERNAME],
            'user_pass' => !empty($data[ClientFields::PASSWORD]) ? $data[ClientFields::PASSWORD] : wp_generate_password(),
            'display_name' => $data[ClientFields::FIRST_NAME] . ' ' . $data[ClientFields::LAST_NAME],
            'first_name' => $data[ClientFields::FIRST_NAME],
            'last_name' => $data[ClientFields::LAST_NAME],
            'role' => 'customer',
        ];
        
        $user_id = wp_insert_user($user_data);
        
        if (is_wp_error($user_id)) {
            return null;
        }
        
        // Create client model
        $client = new Client($user_id);
        
        // Set the client data
        $this->setClientData($client, $data);
        
        return $client;
    }
    
    /**
     * Update an existing client
     *
     * @param int $id The client ID
     * @param array $data The client data
     * @return Client|null The updated client model or null if not found
     */
    public function update($id, $data) {
        $client = $this->find($id);
        
        if (!$client) {
            return null;
        }
        
        // Regular client - update user data if email is provided
        $user_data = ['ID' => $id];
        if (!empty($data[ClientFields::EMAIL])) {
            $user_data['user_email'] = $data[ClientFields::EMAIL];
        }
        if (!empty($data[ClientFields::USERNAME])) {
            $user_data['user_login'] = $data[ClientFields::USERNAME];
        }
        if (!empty($data[ClientFields::PASSWORD])) {
            $user_data['user_pass'] = $data[ClientFields::PASSWORD];
        }
        if (!empty($data[ClientFields::FIRST_NAME]) && !empty($data[ClientFields::LAST_NAME])) {
            $user_data['display_name'] = $data[ClientFields::FIRST_NAME] . ' ' . $data[ClientFields::LAST_NAME];
        }
        if (!empty($data[ClientFields::FIRST_NAME])) {
            $user_data['first_name'] = $data[ClientFields::FIRST_NAME];
        }
        if (!empty($data[ClientFields::LAST_NAME])) {
            $user_data['last_name'] = $data[ClientFields::LAST_NAME];
        }

        // Only update user data if we have more than just the ID
        if(count($user_data) > 1) {
            $result = wp_update_user($user_data);
            if (is_wp_error($result)) {
                // Log the error but continue with client data update
            }
        }
        
        // Set the client data
        $this->setClientData($client, $data);
        
        return $client;
    }
    
    /**
     * Delete a client
     *
     * @param int $id The client ID
     * @return bool True if successful, false otherwise
     */
    public function delete($id) {
        // Check if client exists
        $client = $this->find($id);
        
        if (!$client) {
            return false;
        }
        
        // Check if user exists and is not an administrator
        $user = get_user_by('ID', $id);
        if (!$user || in_array('administrator', $user->roles)) {
            return false;
        }
        
        // Delete all invoices and quotes associated with this client
        global $wpdb;
        
        // Get all invoices and quotes for this client
        $posts = $wpdb->get_results($wpdb->prepare(
            "SELECT ID, post_type FROM {$wpdb->posts} WHERE post_type IN ('easy_invoice', 'easy_invoice_quote') AND ID IN (
                SELECT post_id FROM {$wpdb->postmeta} 
                WHERE (meta_key = '_easy_invoice_client_id' OR meta_key = '_easy_invoice_quote_client_id') 
                AND meta_value = %d
            )",
            $id
        ));
        
        // Delete each post and its meta
        foreach ($posts as $post) {
            wp_delete_post($post->ID, true);
        }
        
        // Delete all payments associated with this client
        $payments = $wpdb->get_col($wpdb->prepare(
            "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'easy_payment' AND ID IN (
                SELECT post_id FROM {$wpdb->postmeta} 
                WHERE meta_key = '_easy_payment_client_id' AND meta_value = %d
            )",
            $id
        ));
        
        foreach ($payments as $payment_id) {
            wp_delete_post($payment_id, true);
        }
        
        // Delete the WordPress user (this will also delete all user meta)
        $result = wp_delete_user($id);
        
        // Return the result
        return $result;
        
        return $result;
    }
    
    /**
     * Find clients by email
     *
     * @param string $email The client email
     * @return array Array of Client models
     */
    public function findByEmail($email) {
        $clients = [];
        
        // Find regular clients by email
        $user = get_user_by('email', $email);
        
        if ($user) {
            $clients[] = $this->createClientFromUser($user);
        }
        
        return $clients;
    }
    
    /**
     * Find clients by business/client name
     *
     * @param string $business_client_name The business/client name
     * @return array Array of Client models
     */
    public function findByBusinessClientName($business_client_name) {
        $args = [
            'meta_query' => [
                [
                    'key' => ClientFields::BUSINESS_CLIENT_NAME,
                    'value' => $business_client_name,
                    'compare' => 'LIKE',
                ],
            ],
        ];
        
        return $this->all($args);
    }
    
    /**
     * Search clients by name, email, or company
     *
     * @param string $query The search query
     * @return array Array of Client models
     */
    public function search($query) {
        $clients = [];
        $query = trim($query);
        
        if (empty($query)) {
            return $this->all();
        }
        
        // Search by display name, first name, last name, or email
        $name_args = [
            'search' => '*' . $query . '*',
            'search_columns' => ['display_name', 'first_name', 'last_name', 'user_email'],
            'meta_query' => [
                'relation' => 'OR',
                [
                    'key' => ClientFields::BUSINESS_CLIENT_NAME,
                    'compare' => 'EXISTS',
                ],
                [
                    'key' => ClientFields::EMAIL,
                    'compare' => 'EXISTS',
                ],
            ],
        ];
        
        $name_query = new WP_User_Query($name_args);
        foreach ($name_query->get_results() as $user) {
            $clients[] = $this->createClientFromUser($user);
        }
        
        // Search by business client name (meta field)
        $business_args = [
            'meta_query' => [
                'relation' => 'AND',
                [
                    'key' => ClientFields::BUSINESS_CLIENT_NAME,
                    'value' => $query,
                    'compare' => 'LIKE',
                ],
                [
                    'relation' => 'OR',
                    [
                        'key' => ClientFields::BUSINESS_CLIENT_NAME,
                        'compare' => 'EXISTS',
                    ],
                    [
                        'key' => ClientFields::EMAIL,
                        'compare' => 'EXISTS',
                    ],
                ],
            ],
        ];
        
        $business_query = new WP_User_Query($business_args);
        foreach ($business_query->get_results() as $user) {
            // Check if this client is already in the results
            $exists = false;
            foreach ($clients as $existing_client) {
                if ($existing_client->getId() === $user->ID) {
                    $exists = true;
                    break;
                }
            }
            
            if (!$exists) {
                $clients[] = $this->createClientFromUser($user);
            }
        }
        
        // Search by email (meta field)
        $email_args = [
            'meta_query' => [
                'relation' => 'AND',
                [
                    'key' => ClientFields::EMAIL,
                    'value' => $query,
                    'compare' => 'LIKE',
                ],
                [
                    'relation' => 'OR',
                    [
                        'key' => ClientFields::BUSINESS_CLIENT_NAME,
                        'compare' => 'EXISTS',
                    ],
                    [
                        'key' => ClientFields::EMAIL,
                        'compare' => 'EXISTS',
                    ],
                ],
            ],
        ];
        
        $email_query = new WP_User_Query($email_args);
        foreach ($email_query->get_results() as $user) {
            // Check if this client is already in the results
            $exists = false;
            foreach ($clients as $existing_client) {
                if ($existing_client->getId() === $user->ID) {
                    $exists = true;
                    break;
                }
            }
            
            if (!$exists) {
                $clients[] = $this->createClientFromUser($user);
            }
        }
        
        // Limit results to 10 to avoid performance issues
        return array_slice($clients, 0, 10);
    }
    
    /**
     * Create a client model from a WordPress user
     *
     * @param WP_User $user The WordPress user
     * @return Client The client model
     */
    protected function createClientFromUser(WP_User $user) {
        try {
            $client = new Client($user->ID);
            return $client;
        } catch (\Exception $e) {
            return null;
        }
    }
    
    /**
     * Set the client data
     *
     * @param Client $client The client model
     * @param array $data The client data
     */
    protected function setClientData(Client $client, array $data) {
        if (isset($data[ClientFields::BUSINESS_CLIENT_NAME])) {
            $client->business_client_name = $data[ClientFields::BUSINESS_CLIENT_NAME];
            update_user_meta($client->getId(), ClientFields::BUSINESS_CLIENT_NAME, $data[ClientFields::BUSINESS_CLIENT_NAME]);
        }
        
        if (isset($data[ClientFields::EMAIL])) {
            $client->email = $data[ClientFields::EMAIL];
            update_user_meta($client->getId(), ClientFields::EMAIL, $data[ClientFields::EMAIL]);
        }

        if (isset($data[ClientFields::USERNAME])) {
            $client->username = $data[ClientFields::USERNAME];
            update_user_meta($client->getId(), ClientFields::USERNAME, $data[ClientFields::USERNAME]);
        }

        if (isset($data[ClientFields::ADDRESS])) {
            $client->address = $data[ClientFields::ADDRESS];
            update_user_meta($client->getId(), ClientFields::ADDRESS, $data[ClientFields::ADDRESS]);
        }

        if (isset($data[ClientFields::EXTRA_INFO])) {
            $client->extra_info = $data[ClientFields::EXTRA_INFO];
            update_user_meta($client->getId(), ClientFields::EXTRA_INFO, $data[ClientFields::EXTRA_INFO]);
        }

        if (isset($data[ClientFields::FIRST_NAME])) {
            $client->first_name = $data[ClientFields::FIRST_NAME];
            update_user_meta($client->getId(), ClientFields::FIRST_NAME, $data[ClientFields::FIRST_NAME]);
        }

        if (isset($data[ClientFields::LAST_NAME])) {
            $client->last_name = $data[ClientFields::LAST_NAME];
            update_user_meta($client->getId(), ClientFields::LAST_NAME, $data[ClientFields::LAST_NAME]);
        }

        if (isset($data[ClientFields::WEBSITE])) {
            $client->website = $data[ClientFields::WEBSITE];
            update_user_meta($client->getId(), ClientFields::WEBSITE, $data[ClientFields::WEBSITE]);
        }
        if (isset($data[ClientFields::PHONE])) {
            $client->phone = $data[ClientFields::PHONE];
            update_user_meta($client->getId(), ClientFields::PHONE, $data[ClientFields::PHONE]);
        }
        
        // Reset the dirty flag after saving
        $client->resetDirty();
    }
} 