<?php

namespace BookWellAB;

defined('ABSPATH') || exit;

if (!class_exists('BookWellAB_Customer_Account')) {

    class BookWellAB_Customer_Account
    {
        public const ROLE = 'bookwellnow-customer';

        /**
         * Register the BookWellNow customer role (minimal frontend permissions).
         *
         * @return void
         */
        public static function ensure_customer_role()
        {
            if (get_role(self::ROLE)) {
                return;
            }

            add_role(
                self::ROLE,
                __('BookWellNow Customer', 'bookwellnow-appointment-booking'),
                [
                    'read' => true,
                    'level_0' => true,
                    self::ROLE => true,
                ]
            );
        }

        /**
         * Whether WP user creation on booking is enabled.
         *
         * @return bool
         */
        public static function is_wp_user_creation_enabled()
        {
            $settings = BookWellAB_Settings::get_settings();
            $customer_panel = isset($settings['customer_panel']) && is_array($settings['customer_panel'])
                ? $settings['customer_panel']
                : [];

            return !empty($customer_panel['create_wp_user_on_booking']);
        }

        /**
         * Build a customer prefill payload for the frontend booking form.
         *
         * Used to auto-populate the "Your Details" step when a logged-in user
         * already has a linked BookWellNow customer record (matched by WP user
         * ID first, then by email). Falls back to the WP user profile fields.
         *
         * @return array<string, string>|null
         */
        public static function get_booking_prefill_customer()
        {
            if (!is_user_logged_in()) {
                return null;
            }

            $user = wp_get_current_user();
            if (!$user instanceof \WP_User) {
                return null;
            }

            $customer = self::get_customer_by_wp_user_id((int) $user->ID);

            if (!$customer && !empty($user->user_email)) {
                global $wpdb;
                $bwdb = new BookWellAB_Database();
                // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Dynamic plugin-owned table name.
                $row = $wpdb->get_row(
                    $wpdb->prepare(
                        'SELECT * FROM `' . esc_sql($bwdb->get_table_name('customers')) . '` WHERE bw_email = %s LIMIT 1',
                        sanitize_email($user->user_email)
                    ),
                    ARRAY_A
                );
                if (is_array($row)) {
                    $customer = $row;
                }
            }

            if (is_array($customer)) {
                return [
                    'first_name' => (string) ($customer['bw_first_name'] ?? ''),
                    'last_name' => (string) ($customer['bw_last_name'] ?? ''),
                    'email' => (string) ($customer['bw_email'] ?? ''),
                    'phone' => (string) ($customer['bw_phone'] ?? ''),
                    'phone_code' => (string) ($customer['bw_phone_code'] ?? ''),
                    'country_slug' => (string) ($customer['bw_country_slug'] ?? ''),
                ];
            }

            return [
                'first_name' => (string) $user->first_name,
                'last_name' => (string) $user->last_name,
                'email' => (string) $user->user_email,
                'phone' => '',
                'phone_code' => '',
                'country_slug' => '',
            ];
        }

        /**
         * @param int $wp_user_id WordPress user ID.
         * @return array<string, mixed>|null
         */
        public static function get_customer_by_wp_user_id($wp_user_id)
        {
            global $wpdb;

            $wp_user_id = absint($wp_user_id);
            if ($wp_user_id <= 0) {
                return null;
            }

            $bwdb = new BookWellAB_Database();
            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Dynamic plugin-owned table name.
            $row = $wpdb->get_row(
                $wpdb->prepare(
                    'SELECT * FROM `' . esc_sql($bwdb->get_table_name('customers')) . '` WHERE bw_wp_user_id = %d LIMIT 1',
                    $wp_user_id
                ),
                ARRAY_A
            );

            return is_array($row) ? $row : null;
        }

        /**
         * @param int $customer_id Customer ID.
         * @return array<string, mixed>|null
         */
        public static function get_customer_by_id($customer_id)
        {
            global $wpdb;

            $customer_id = absint($customer_id);
            if ($customer_id <= 0) {
                return null;
            }

            $bwdb = new BookWellAB_Database();
            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Dynamic plugin-owned table name.
            $row = $wpdb->get_row(
                $wpdb->prepare(
                    'SELECT * FROM `' . esc_sql($bwdb->get_table_name('customers')) . '` WHERE bw_customer_id = %d LIMIT 1',
                    $customer_id
                ),
                ARRAY_A
            );

            return is_array($row) ? $row : null;
        }

        /**
         * Create or link a WordPress user for a BookWell customer when the setting is enabled.
         *
         * @param int                  $customer_id Customer row ID.
         * @param array<string, mixed> $customer_data first_name, last_name, email.
         * @return int WordPress user ID or 0.
         */
        public static function maybe_create_wp_user_for_customer($customer_id, array $customer_data)
        {
            if (!self::is_wp_user_creation_enabled()) {
                return 0;
            }

            $customer_id = absint($customer_id);
            if ($customer_id <= 0) {
                return 0;
            }

            $customer = self::get_customer_by_id($customer_id);
            if (!$customer) {
                return 0;
            }

            $existing_wp_user_id = isset($customer['bw_wp_user_id']) ? absint($customer['bw_wp_user_id']) : 0;
            if ($existing_wp_user_id > 0 && get_userdata($existing_wp_user_id)) {
                return $existing_wp_user_id;
            }

            $email = isset($customer_data['email']) ? sanitize_email((string) $customer_data['email']) : '';
            if ($email === '') {
                $email = isset($customer['bw_email']) ? sanitize_email((string) $customer['bw_email']) : '';
            }
            if ($email === '' || !is_email($email)) {
                return 0;
            }

            $first_name = isset($customer_data['first_name'])
                ? sanitize_text_field((string) $customer_data['first_name'])
                : (isset($customer['bw_first_name']) ? (string) $customer['bw_first_name'] : '');
            $last_name = isset($customer_data['last_name'])
                ? sanitize_text_field((string) $customer_data['last_name'])
                : (isset($customer['bw_last_name']) ? (string) $customer['bw_last_name'] : '');

            $wp_user = get_user_by('email', $email);
            if ($wp_user instanceof \WP_User) {
                $wp_user_id = (int) $wp_user->ID;
                if (!$wp_user->has_role(self::ROLE)) {
                    $wp_user->add_role(self::ROLE);
                }
            } else {
                $username = self::generate_unique_username($email, $first_name, $last_name);

                $password = isset($customer_data['password']) ? (string) $customer_data['password'] : '';
                $password = trim($password);

                /**
                 * Filters the password used when creating a WP user from a booking.
                 * Empty string means generate a random password (default).
                 *
                 * @param string               $password      Provided password or empty.
                 * @param array<string, mixed> $customer_data Customer payload.
                 * @param int                  $customer_id   Customer ID.
                 */
                $password = (string) apply_filters('bookwellab_create_wp_user_password', $password, $customer_data, $customer_id);
                $password = trim($password);

                if ($password === '' || strlen($password) < 6) {
                    $password = wp_generate_password(12, true, true);
                }

                $wp_user_id = wp_create_user($username, $password, $email);

                if (is_wp_error($wp_user_id)) {
                    return 0;
                }

                $wp_user_id = (int) $wp_user_id;
                wp_update_user([
                    'ID' => $wp_user_id,
                    'first_name' => $first_name,
                    'last_name' => $last_name,
                    'display_name' => trim($first_name . ' ' . $last_name),
                ]);

                $user = new \WP_User($wp_user_id);
                $user->set_role(self::ROLE);

                wp_send_new_user_notifications($wp_user_id, 'user');
            }

            self::link_customer_to_wp_user($customer_id, $wp_user_id);

            return $wp_user_id;
        }

        /**
         * @param int $customer_id Customer ID.
         * @param int $wp_user_id  WordPress user ID.
         * @return bool
         */
        public static function link_customer_to_wp_user($customer_id, $wp_user_id)
        {
            global $wpdb;

            $customer_id = absint($customer_id);
            $wp_user_id = absint($wp_user_id);
            if ($customer_id <= 0 || $wp_user_id <= 0) {
                return false;
            }

            $bwdb = new BookWellAB_Database();
            $table = $bwdb->get_table_name('customers');

            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Updating customer link in plugin-owned table.
            $updated = $wpdb->update(
                $table,
                ['bw_wp_user_id' => $wp_user_id],
                ['bw_customer_id' => $customer_id],
                ['%d'],
                ['%d']
            );

            return $updated !== false;
        }

        /**
         * @param string $email      Customer email.
         * @param string $first_name First name.
         * @param string $last_name  Last name.
         * @return string
         */
        private static function generate_unique_username($email, $first_name, $last_name)
        {
            $base = sanitize_user(current(explode('@', $email)), true);
            if ($base === '') {
                $base = sanitize_user(strtolower($first_name . $last_name), true);
            }
            if ($base === '') {
                $base = 'bookwell_customer';
            }

            $username = $base;
            $suffix = 1;
            while (username_exists($username)) {
                $username = $base . $suffix;
                ++$suffix;
            }

            return $username;
        }

        /**
         * Resolve the logged-in panel customer row.
         *
         * @return array<string, mixed>|null
         */
        public static function get_current_panel_customer()
        {
            if (!is_user_logged_in()) {
                return null;
            }

            $user = wp_get_current_user();
            if (!$user instanceof \WP_User) {
                return null;
            }

            $customer = self::get_customer_by_wp_user_id((int) $user->ID);
            if ($customer) {
                return $customer;
            }

            // Auto-link any logged-in user when customer email matches WP user email.
            $linked = self::maybe_link_customer_by_email($user);
            if (is_array($linked)) {
                return $linked;
            }

            if (!empty($user->user_email)) {
                global $wpdb;
                $bwdb = new BookWellAB_Database();
                $email = strtolower(sanitize_email($user->user_email));
                // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Dynamic plugin-owned table name.
                $row = $wpdb->get_row(
                    $wpdb->prepare(
                        'SELECT * FROM `' . esc_sql($bwdb->get_table_name('customers')) . '` WHERE LOWER(bw_email) = %s LIMIT 1',
                        $email
                    ),
                    ARRAY_A
                );
                if (is_array($row)) {
                    // Keep panel access flexible: show appointments by matched email even when not role-linked yet.
                    return $row;
                }
            }

            // No existing customer row — create one for this logged-in WP user.
            return self::create_customer_for_wp_user($user);
        }

        /**
         * Create a BookWell customer row for a logged-in WordPress user.
         *
         * @param \WP_User $user WordPress user.
         * @return array<string, mixed>|null
         */
        public static function create_customer_for_wp_user($user)
        {
            if (!$user instanceof \WP_User || $user->ID <= 0) {
                return null;
            }

            $email = sanitize_email((string) $user->user_email);
            if ($email === '' || !is_email($email)) {
                return null;
            }

            // Race-safe: another request may have created/linked the customer already.
            $existing = self::get_customer_by_wp_user_id((int) $user->ID);
            if (is_array($existing)) {
                return $existing;
            }

            global $wpdb;
            $bwdb = new BookWellAB_Database();
            $table = $bwdb->get_table_name('customers');

            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Dynamic plugin-owned table name.
            $by_email = $wpdb->get_row(
                $wpdb->prepare(
                    'SELECT * FROM `' . esc_sql($table) . '` WHERE LOWER(bw_email) = %s LIMIT 1',
                    strtolower($email)
                ),
                ARRAY_A
            );
            if (is_array($by_email) && !empty($by_email['bw_customer_id'])) {
                $linked_wp_user_id = isset($by_email['bw_wp_user_id']) ? absint($by_email['bw_wp_user_id']) : 0;
                if ($linked_wp_user_id === 0 || !get_userdata($linked_wp_user_id)) {
                    self::link_customer_to_wp_user((int) $by_email['bw_customer_id'], (int) $user->ID);
                    $by_email['bw_wp_user_id'] = (int) $user->ID;
                }
                self::ensure_user_has_customer_role($user);
                return $by_email;
            }

            $first_name = sanitize_text_field((string) $user->first_name);
            $last_name = sanitize_text_field((string) $user->last_name);
            if ($first_name === '' && $last_name === '') {
                $display = trim((string) $user->display_name);
                if ($display === '' || $display === $user->user_login) {
                    $display = (string) $user->user_login;
                }
                $parts = preg_split('/\s+/', $display, 2);
                $first_name = isset($parts[0]) ? sanitize_text_field((string) $parts[0]) : 'Customer';
                $last_name = isset($parts[1]) ? sanitize_text_field((string) $parts[1]) : '';
            }
            if ($first_name === '') {
                $first_name = 'Customer';
            }

            $data = [
                'bw_first_name' => $first_name,
                'bw_last_name' => $last_name,
                'bw_email' => $email,
                'bw_phone_code' => '',
                'bw_phone' => '',
                'bw_country_slug' => '',
                'bw_wp_user_id' => (int) $user->ID,
            ];

            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Creating customer row in plugin-owned table.
            $inserted = $wpdb->insert(
                $table,
                $data,
                ['%s', '%s', '%s', '%s', '%s', '%s', '%d']
            );

            if (!$inserted) {
                // Unique email race: fetch the row created by a parallel request.
                // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Dynamic plugin-owned table name.
                $race = $wpdb->get_row(
                    $wpdb->prepare(
                        'SELECT * FROM `' . esc_sql($table) . '` WHERE LOWER(bw_email) = %s LIMIT 1',
                        strtolower($email)
                    ),
                    ARRAY_A
                );
                return is_array($race) ? $race : null;
            }

            self::ensure_user_has_customer_role($user);

            $customer_id = (int) $wpdb->insert_id;
            $row = self::get_customer_by_id($customer_id);

            return is_array($row) ? $row : null;
        }

        /**
         * Auto-link a customer record to a WP user when their emails match.
         *
         * Finds a customer by email and links it when unlinked, orphaned, or already
         * tied to the same WP user. Skips when the customer is linked to another user.
         *
         * @param \WP_User $user WordPress user.
         * @return array<string, mixed>|null
         */
        public static function maybe_link_customer_by_email($user)
        {
            if (!$user instanceof \WP_User) {
                return null;
            }

            $email = strtolower(sanitize_email((string) $user->user_email));
            if ($email === '') {
                return null;
            }

            global $wpdb;
            $bwdb = new BookWellAB_Database();
            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Dynamic plugin-owned table name.
            $row = $wpdb->get_row(
                $wpdb->prepare(
                    'SELECT * FROM `' . esc_sql($bwdb->get_table_name('customers')) . '` WHERE LOWER(bw_email) = %s LIMIT 1',
                    $email
                ),
                ARRAY_A
            );

            if (!is_array($row) || empty($row['bw_customer_id'])) {
                return null;
            }

            $customer_id = (int) $row['bw_customer_id'];
            $linked_wp_user_id = isset($row['bw_wp_user_id']) ? absint($row['bw_wp_user_id']) : 0;
            $current_wp_user_id = (int) $user->ID;

            if ($linked_wp_user_id === $current_wp_user_id) {
                self::ensure_user_has_customer_role($user);
                return $row;
            }

            if ($linked_wp_user_id > 0) {
                $linked_user = get_userdata($linked_wp_user_id);
                if ($linked_user instanceof \WP_User) {
                    return null;
                }
            }

            if (!self::link_customer_to_wp_user($customer_id, $current_wp_user_id)) {
                return null;
            }

            self::ensure_user_has_customer_role($user);

            $row['bw_wp_user_id'] = $current_wp_user_id;

            return $row;
        }

        /**
         * Grant the BookWellNow customer role when missing.
         *
         * @param \WP_User $user WordPress user.
         * @return void
         */
        private static function ensure_user_has_customer_role($user)
        {
            if (!$user instanceof \WP_User) {
                return;
            }

            if (!$user->has_role(self::ROLE) && !user_can($user, 'manage_options')) {
                $user->add_role(self::ROLE);
            }
        }

        /**
         * Auto-link on WordPress login so the relation exists before any panel render.
         *
         * @param string        $user_login Username.
         * @param \WP_User|null $user       Logged-in user.
         * @return void
         */
        public static function on_wp_login($user_login, $user = null)
        {
            if (!$user instanceof \WP_User) {
                $user = get_user_by('login', $user_login);
            }
            if (!$user instanceof \WP_User) {
                return;
            }

            if (self::get_customer_by_wp_user_id((int) $user->ID)) {
                return;
            }

            self::maybe_link_customer_by_email($user);
        }
    }
}
