<?php
/**
 * Client Migration for Easy Invoice
 *
 * @package     EasyInvoice
 * @subpackage  Migration
 * @since       2.0.0
 */

namespace EasyInvoice\Migration\Src;

/**
 * Client migration class.
 *
 * Handles migration of clients to WordPress users.
 *
 * @since 2.0.0
 */
class ClientMigration extends AbstractMigration {

    /**
     * Migration description.
     *
     * @since 2.0.0
     * @var string
     */
    protected $description = 'Migrate clients to WordPress users';

    /**
     * Check if migration is needed.
     *
     * @since 2.0.0
     * @return bool
     */
    public function is_needed(): bool {
        global $wpdb;

        // Check if any old invoices or quotes have client data that needs migration
        $count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT pm.meta_value)
            FROM {$wpdb->postmeta} pm
            JOIN {$wpdb->posts} p ON p.ID = pm.post_id
            WHERE p.post_type IN ('easy-invoice', 'easy-invoice-quotes')
            AND pm.meta_key IN ('client_email', '_client_email')
            AND pm.meta_value != ''
            AND NOT EXISTS (
                SELECT 1 FROM {$wpdb->users} u
                WHERE u.user_email = pm.meta_value
            )"
        ));

        if ($count > 0) {
            $this->log(sprintf('Found %d unique client emails that need migration to WordPress users', $count));
                            return true;
                        }

        // Also check for any client data in meta that hasn't been migrated to user meta
        $count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT pm.post_id)
            FROM {$wpdb->postmeta} pm
            JOIN {$wpdb->posts} p ON p.ID = pm.post_id
            WHERE p.post_type IN ('easy-invoice', 'easy-invoice-quotes')
            AND pm.meta_key IN (
                'client_name', '_client_name',
                'client_company', '_client_company',
                'client_address', '_client_address',
                'client_phone', '_client_phone',
                'client_vat', '_client_vat'
            )
            AND pm.meta_value != ''
            AND NOT EXISTS (
                SELECT 1 FROM {$wpdb->postmeta} pm2
                WHERE pm2.post_id = pm.post_id
                AND pm2.meta_key IN ('_easy_invoice_client_id', '_easy_invoice_quote_client_id')
            )"
        ));

        if ($count > 0) {
            $this->log(sprintf('Found %d documents with client data that needs migration', $count));
            return true;
        }

        return false;
    }

    /**
     * Run the migration.
     *
     * @since 2.0.0
     * @return array
     */
    public function migrate(): array {
        try {
            global $wpdb;

            $this->log('Starting client migration');

            // Get all unique client emails from invoices and quotes
            $client_emails = $wpdb->get_col($wpdb->prepare(
                "SELECT DISTINCT pm.meta_value
                FROM {$wpdb->postmeta} pm
                JOIN {$wpdb->posts} p ON p.ID = pm.post_id
                WHERE p.post_type IN ('easy-invoice', 'easy-invoice-quotes')
                AND pm.meta_key IN ('client_email', '_client_email', 'easy_invoice_client_email', 'easy_invoice_quote_client_email')
                AND pm.meta_value != ''
                AND pm.meta_value != '0'
                AND pm.meta_value IS NOT NULL
                AND NOT EXISTS (
                    SELECT 1 FROM {$wpdb->users} u
                    WHERE u.user_email = pm.meta_value
                )"
            ));

            $migrated_count = 0;
            $errors = [];

            // Process each unique client email
            foreach ($client_emails as $email) {
                // Get all documents for this client
                $documents = $wpdb->get_results($wpdb->prepare(
                    "SELECT p.ID, p.post_type
                    FROM {$wpdb->posts} p
                    JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
                    WHERE p.post_type IN ('easy-invoice', 'easy-invoice-quotes')
                    AND pm.meta_key IN ('client_email', '_client_email', 'easy_invoice_client_email', 'easy_invoice_quote_client_email')
                    AND pm.meta_value = %s",
                    $email
                ));

                if (empty($documents)) {
                    continue;
                }

                // Get client data from the first document
                $first_doc = $documents[0];
                $client_data = [
                    'email' => $email,
                    'name' => '',
                    'company' => '',
                    'address' => '',
                    'phone' => '',
                    'vat' => '',
                    'website' => '',
                    'additional_info' => ''
                ];

                // Try to get client details from meta
                $meta_keys = [
                    'name' => ['client_name', '_client_name', 'easy_invoice_client_name', 'easy_invoice_quote_client_name', 'name', '_name', 'customer_name', '_customer_name'],
                    'company' => ['client_company', '_client_company', 'easy_invoice_client_company', 'easy_invoice_quote_client_company', 'company', '_company'],
                    'address' => ['client_address', '_client_address', 'easy_invoice_client_address', 'easy_invoice_quote_client_address', 'address', '_address'],
                    'phone' => ['client_phone', '_client_phone', 'easy_invoice_client_phone', 'easy_invoice_quote_client_phone', 'phone', '_phone'],
                    'vat' => ['client_vat', '_client_vat', 'easy_invoice_client_vat', 'easy_invoice_quote_client_vat', 'vat', '_vat'],
                    'website' => ['client_website', '_client_website', 'easy_invoice_client_website', 'easy_invoice_quote_client_website', 'website', '_website'],
                    'additional_info' => ['client_extra_info', '_client_extra_info', 'easy_invoice_client_extra_info', 'easy_invoice_quote_client_extra_info', 'extra_info', '_extra_info', 'additional_info', '_additional_info']
                ];

                foreach ($meta_keys as $field => $possible_keys) {
                    foreach ($possible_keys as $key) {
                        $value = get_post_meta($first_doc->ID, $key, true);
                        if (!empty($value)) {
                            $client_data[$field] = $value;
                            break;
                        }
                    }
                }

                // Create or update WordPress user
                $user_id = $this->get_or_create_user($client_data);

                if ($user_id > 0) {
                    // Update client ID in all documents
                    foreach ($documents as $doc) {
                        if ($doc->post_type === 'easy-invoice') {
                            update_post_meta($doc->ID, '_easy_invoice_client_id', $user_id);
                            // Also update the old meta key for backward compatibility
                            update_post_meta($doc->ID, 'client_email', $client_data['email']);
                        } else {
                            update_post_meta($doc->ID, '_easy_invoice_quote_client_id', $user_id);
                            // Also update the old meta key for backward compatibility
                            update_post_meta($doc->ID, 'client_email', $client_data['email']);
                        }

                        // Store mapping for future reference
                        update_option('easy_invoice_client_user_mapping_' . $doc->ID, $user_id);
                        update_user_meta($user_id, '_easy_invoice_original_client_id', $doc->ID);
                    }

                    $migrated_count++;
                    $this->log(sprintf('Successfully migrated client with email %s to user ID %d', $email, $user_id));
                } else {
                    $errors[] = sprintf('Failed to create/update user for client with email %s', $email);
                }
            }

            if (empty($errors)) {
                return [
                    'success' => true,
                    'message' => sprintf('Successfully migrated %d clients', $migrated_count),
                    'count' => $migrated_count
                ];
            } else {
            return [
                    'success' => false,
                    'message' => 'Client migration completed with errors: ' . implode(', ', $errors),
                    'count' => $migrated_count
                ];
            }

        } catch (\Exception $e) {
            $this->log('Client migration failed: ' . $e->getMessage(), 'error');
            return [
                'success' => false,
                'message' => $e->getMessage(),
                'count' => 0
            ];
        }
    }

    /**
     * Migrate a single client to WordPress user.
     *
     * @since 2.0.0
     * @param object $client Client post object
     * @return array
     */
    private function migrate_single_client($client): array {
        try {
            // Get client meta with fallbacks for different key formats
            $meta_keys = [
                'name' => ['client_name', '_client_name'],
                'email' => ['client_email', '_client_email'],
                'company' => ['client_company', '_client_company'],
                'address' => ['client_address', '_client_address'],
                'phone' => ['client_phone', '_client_phone'],
                'vat' => ['client_vat', '_client_vat']
            ];

            $client_data = [];
            foreach ($meta_keys as $key => $possible_keys) {
                foreach ($possible_keys as $meta_key) {
                    $value = get_post_meta($client->ID, $meta_key, true);
                    if (!empty($value)) {
                        $client_data[$key] = $value;
                        break;
                    }
                }
            }

            // Use post title as name if no name meta found
            $client_name = $client_data['name'] ?? $client->post_title;
            $client_email = $client_data['email'] ?? '';

            // If no email, generate a unique one
            if (empty($client_email)) {
                $client_email = 'client_' . $client->ID . '@example.com';
            }

            // Process client name
            $name_data = $this->process_client_name($client_name);

            // Check if user already exists with this email
            $existing_user = get_user_by('email', $client_email);
            if ($existing_user) {
                // Update user meta
                $this->update_user_meta($existing_user->ID, [
                    'business_client_name' => $client_name, // Full name as business name
                    'first_name' => $name_data['first_name'],
                    'last_name' => $name_data['last_name'],
                    'company' => $client_data['company'] ?? '',
                    'address' => $client_data['address'] ?? '',
                    'phone' => $client_data['phone'] ?? '',
                    'vat' => $client_data['vat'] ?? '',
                    'website' => $client_data['website'] ?? '',
                    'additional_info' => $client_data['additional_info'] ?? ''
                ]);

                // Store mapping
                update_option('easy_invoice_client_user_mapping_' . $client->ID, $existing_user->ID);

                // Store reverse mapping
                update_user_meta($existing_user->ID, '_easy_invoice_original_client_id', $client->ID);

                $this->log(sprintf('Updated existing user %d for client %d', $existing_user->ID, $client->ID));

                return [
                    'success' => true,
                    'message' => sprintf('Updated existing user %d for client %d', $existing_user->ID, $client->ID),
                    'user_id' => $existing_user->ID
                ];
            }

            // Create username from email or name
            $username = sanitize_user(current(explode('@', $client_email)), true);
            if (username_exists($username)) {
                $username = $username . '_' . $client->ID;
            }

            // Create random password
            $password = wp_generate_password();

            // Create user
            $user_data = [
                'user_login' => $username,
                'user_email' => $client_email,
                'user_pass' => $password,
                'display_name' => $client_name,
                'role' => 'easy_invoice_client'
            ];

            $user_id = wp_insert_user($user_data);

            if (is_wp_error($user_id)) {
                return [
                    'success' => false,
                    'message' => $user_id->get_error_message()
                ];
            }

            // Add user meta
            $this->update_user_meta($user_id, [
                'business_client_name' => $client_name, // Set the client name as business name
                'company' => $client_data['company'] ?? '',
                'address' => $client_data['address'] ?? '',
                'phone' => $client_data['phone'] ?? '',
                'vat' => $client_data['vat'] ?? '',
                'additional_info' => $client_data['additional_info'] ?? ''
            ]);

            // Store mappings
            update_option('easy_invoice_client_user_mapping_' . $client->ID, $user_id);
            update_user_meta($user_id, '_easy_invoice_original_client_id', $client->ID);

            $this->log(sprintf('Created user %d for client %d', $user_id, $client->ID));

            return [
                'success' => true,
                'message' => sprintf('Created user %d for client %d', $user_id, $client->ID),
                'user_id' => $user_id
            ];

        } catch (\Exception $e) {
        return [
                'success' => false,
                'message' => $e->getMessage()
        ];
        }
    }

    /**
     * Update user meta with client data.
     *
     * @since 2.0.0
     * @param int $user_id User ID
     * @param array $meta Meta data
     * @return void
     */
    private function update_user_meta(int $user_id, array $meta): void {
        foreach ($meta as $key => $value) {
            if (!empty($value)) {
                // Map the key to the correct meta key
                $meta_key = $this->get_meta_key($key);
                if ($meta_key) {
                    update_user_meta($user_id, $meta_key, $value);
                }
            }
        }
    }

    /**
     * Get the correct meta key for a field.
     *
     * @since 2.0.0
     * @param string $field Field name
     * @return string Meta key
     */
    private function get_meta_key(string $field): string {
        $meta_keys = [
            'business_client_name' => '_easy_invoice_client_business_client_name',
            'first_name' => '_easy_invoice_client_first_name',
            'last_name' => '_easy_invoice_client_last_name',
            'company' => '_easy_invoice_client_company',
            'address' => '_easy_invoice_client_address',
            'phone' => '_easy_invoice_client_phone',
            'vat' => '_easy_invoice_client_vat',
            'website' => '_easy_invoice_client_website',
            'additional_info' => '_easy_invoice_client_extra_info'
        ];

        return $meta_keys[$field] ?? '';
    }

    /**
     * Process client name and split into first and last name.
     *
     * @since 2.0.0
     * @param string $full_name Full client name
     * @return array Array with first_name and last_name
     */
    private function process_client_name(string $full_name): array {
        $full_name = trim($full_name);

        if (empty($full_name)) {
            return ['first_name' => '', 'last_name' => ''];
        }

        // Split by space
        $name_parts = explode(' ', $full_name);

        if (count($name_parts) === 1) {
            // Only one name, use as first name
            return ['first_name' => $name_parts[0], 'last_name' => ''];
        } elseif (count($name_parts) === 2) {
            // Two names, first and last
            return ['first_name' => $name_parts[0], 'last_name' => $name_parts[1]];
        } else {
            // More than two names, first name is first part, last name is everything else
            $first_name = array_shift($name_parts);
            $last_name = implode(' ', $name_parts);
            return ['first_name' => $first_name, 'last_name' => $last_name];
        }
    }

    /**
     * Get user ID for client.
     *
     * @since 2.0.0
     * @param int $client_id Old client ID
     * @return int User ID or 0 if not found
     */
    /**
     * Get or create a WordPress user for a client.
     *
     * @since 2.0.0
     * @param array $client_data Client data array with email, name, company, address, phone, vat
     * @return int User ID or 0 if failed
     */
    public function get_or_create_user(array $client_data): int {
        if (empty($client_data['email'])) {
            return 0;
        }

        // Try to find existing user by email
        $user = get_user_by('email', $client_data['email']);
        if ($user) {
            // Process client name
            $name_data = $this->process_client_name($client_data['name'] ?? '');

            // Update user meta
            $this->update_user_meta($user->ID, [
                'business_client_name' => $client_data['name'] ?? '', // Full name as business name
                'first_name' => $name_data['first_name'],
                'last_name' => $name_data['last_name'],
                'company' => $client_data['company'] ?? '',
                'address' => $client_data['address'] ?? '',
                'phone' => $client_data['phone'] ?? '',
                'vat' => $client_data['vat'] ?? '',
                'website' => $client_data['website'] ?? '',
                'additional_info' => $client_data['additional_info'] ?? ''
            ]);

            return $user->ID;
        }

        // Create new user
        $username = sanitize_user(current(explode('@', $client_data['email'])), true);
        if (username_exists($username)) {
            $username = $username . '_' . time();
        }

        // Process client name
        $name_data = $this->process_client_name($client_data['name'] ?? '');

        // If no name provided, use email username part
        $display_name = !empty($client_data['name']) ? $client_data['name'] : current(explode('@', $client_data['email']));

        $user_data = [
            'user_login' => $username,
            'user_email' => $client_data['email'],
            'user_pass' => wp_generate_password(),
            'display_name' => $display_name,
            'first_name' => $name_data['first_name'],
            'last_name' => $name_data['last_name'],
            'role' => 'easy_invoice_client'
        ];

        $user_id = wp_insert_user($user_data);

        if (is_wp_error($user_id)) {
            $this->log(sprintf('Failed to create user for client with email %s: %s', $client_data['email'], $user_id->get_error_message()), 'error');
            return 0;
        }

        // Add user meta
        $this->update_user_meta($user_id, [
            'business_client_name' => $client_data['name'] ?? '', // Full name as business name
            'first_name' => $name_data['first_name'],
            'last_name' => $name_data['last_name'],
            'company' => $client_data['company'] ?? '',
            'address' => $client_data['address'] ?? '',
            'phone' => $client_data['phone'] ?? '',
            'vat' => $client_data['vat'] ?? '',
            'website' => $client_data['website'] ?? '',
            'additional_info' => $client_data['additional_info'] ?? ''
        ]);

        $this->log(sprintf('Created new user %d for client with email %s', $user_id, $client_data['email']));
        return $user_id;
    }

    public function get_user_id_for_client(int $client_id): int {
        if (empty($client_id)) {
            return 0;
        }

        $user_id = get_option('easy_invoice_client_user_mapping_' . $client_id);
        return !empty($user_id) ? (int)$user_id : 0;
    }

    /**
     * Get client meta for user.
     *
     * @since 2.0.0
     * @param int $user_id User ID
     * @return array Client meta
     */
    public function get_client_meta_for_user(int $user_id): array {
        if (empty($user_id)) {
            return [];
        }

        $user = get_user_by('id', $user_id);
        if (!$user) {
            return [];
        }

        return [
            'first_name' => get_user_meta($user_id, '_easy_invoice_client_first_name', true) ?: $user->first_name,
            'last_name' => get_user_meta($user_id, '_easy_invoice_client_last_name', true) ?: $user->last_name,
            'business_client_name' => get_user_meta($user_id, '_easy_invoice_client_business_client_name', true),
            'email' => get_user_meta($user_id, '_easy_invoice_client_email', true) ?: $user->user_email,
            'username' => get_user_meta($user_id, '_easy_invoice_client_username', true) ?: $user->user_login,
            'address' => get_user_meta($user_id, '_easy_invoice_client_address', true),
            'extra_info' => get_user_meta($user_id, '_easy_invoice_client_extra_info', true),
            'website' => get_user_meta($user_id, '_easy_invoice_client_website', true) ?: $user->user_url,
            'display_name' => $user->display_name,
            'company' => get_user_meta($user_id, '_easy_invoice_client_company', true),
            'phone' => get_user_meta($user_id, '_easy_invoice_client_phone', true),
            'vat' => get_user_meta($user_id, '_easy_invoice_client_vat', true)
        ];
    }
}
