<?php
/**
 * AddressService — user address book CRUD plus validation helpers.
 *
 * Manages tc_user_addresses for registered customers.
 * Also provides country/state lookups and address formatting.
 *
 * @package TitanCart\Services
 */

namespace TitanCart\Services;

defined( 'ABSPATH' ) || exit;

class AddressService {

    /* ──────────────────────────────────────────────────────────────────────────
     * Address book
     * ────────────────────────────────────────────────────────────────────────── */

    /**
     * Get all saved addresses for a user.
     *
     * @param  int         $user_id
     * @param  string|null $type  'billing'|'shipping'|'both'|null (all)
     * @return array
     */
    public static function get_user_addresses( int $user_id, ?string $type = null ): array {
        global $wpdb;
        $table = $wpdb->prefix . 'tc_user_addresses';

        $sql    = "SELECT * FROM `{$table}` WHERE user_id = %d";
        $params = [ $user_id ];

        if ( $type ) {
            $sql     .= ' AND (type = %s OR type = "both")';
            $params[] = $type;
        }

        $sql .= ' ORDER BY is_default DESC, address_id ASC';

        return $wpdb->get_results( $wpdb->prepare( $sql, ...$params ) ) ?: []; // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- $sql interpolates only the constant-built tc_user_addresses table name; placeholders built in lockstep with $params, all bound via prepare() here.
    }

    /**
     * Get a single address by ID, ensuring it belongs to the given user.
     *
     * @param  int $address_id
     * @param  int $user_id
     * @return object|null
     */
    public static function get_address( int $address_id, int $user_id ): ?object {
        global $wpdb;
        $table = $wpdb->prefix . 'tc_user_addresses';
        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM `{$table}` WHERE address_id = %d AND user_id = %d",
            $address_id, $user_id
        ) );
        return $row ?: null;
    }

    /**
     * Create or update an address.
     *
     * @param  int        $user_id
     * @param  array      $data         Address fields.
     * @param  int|null   $address_id   Provide to update; null to create.
     * @return int|false  address_id on success, false on failure.
     */
    public static function save_address( int $user_id, array $data, ?int $address_id = null ) {
        global $wpdb;
        $table = $wpdb->prefix . 'tc_user_addresses';

        $errors = self::validate_address( $data );
        if ( ! empty( $errors ) ) {
            return false;
        }

        $row = [
            'user_id'      => $user_id,
            'type'         => in_array( $data['type'] ?? '', [ 'billing', 'shipping', 'both' ] ) ? $data['type'] : 'both',
            'first_name'   => sanitize_text_field( $data['first_name']  ?? '' ),
            'last_name'    => sanitize_text_field( $data['last_name']   ?? '' ),
            'company'      => sanitize_text_field( $data['company']     ?? '' ),
            'address_1'    => sanitize_text_field( $data['address_1']   ?? '' ),
            'address_2'    => sanitize_text_field( $data['address_2']   ?? '' ),
            'city'         => sanitize_text_field( $data['city']        ?? '' ),
            'state_code'   => sanitize_text_field( $data['state_code']  ?? '' ),
            'zipcode'      => sanitize_text_field( $data['zipcode']     ?? '' ),
            'country_code' => strtoupper( substr( sanitize_text_field( $data['country_code'] ?? '' ), 0, 2 ) ),
            'phone'        => sanitize_text_field( $data['phone']       ?? '' ),
        ];

        if ( $address_id ) {
            // Verify ownership
            $existing = self::get_address( $address_id, $user_id );
            if ( ! $existing ) {
                return false;
            }
            $wpdb->update( $table, $row, [ 'address_id' => $address_id, 'user_id' => $user_id ] );
            return $address_id;
        }

        $wpdb->insert( $table, $row );
        $new_id = (int) $wpdb->insert_id;

        // If it's the first address, auto-set as default
        $count = $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM `{$table}` WHERE user_id = %d",
            $user_id
        ) );
        if ( $count === '1' ) {
            self::set_default( $new_id, $user_id );
        }

        return $new_id;
    }

    /**
     * Delete an address (prevents deleting default if others exist).
     *
     * @param  int $address_id
     * @param  int $user_id
     * @return bool
     */
    public static function delete_address( int $address_id, int $user_id ): bool {
        global $wpdb;
        $table = $wpdb->prefix . 'tc_user_addresses';

        $addr = self::get_address( $address_id, $user_id );
        if ( ! $addr ) {
            return false;
        }

        $wpdb->delete( $table, [ 'address_id' => $address_id, 'user_id' => $user_id ] );

        // If we just deleted the default, promote the next one
        if ( $addr->is_default ) {
            $next = $wpdb->get_var( $wpdb->prepare(
                "SELECT address_id FROM `{$table}` WHERE user_id = %d ORDER BY address_id ASC LIMIT 1",
                $user_id
            ) );
            if ( $next ) {
                self::set_default( (int) $next, $user_id );
            }
        }

        return true;
    }

    /**
     * Mark an address as the default for the user.
     *
     * @param  int $address_id
     * @param  int $user_id
     * @return bool
     */
    public static function set_default( int $address_id, int $user_id ): bool {
        global $wpdb;
        $table = $wpdb->prefix . 'tc_user_addresses';

        // Clear existing defaults
        $wpdb->update( $table, [ 'is_default' => 0 ], [ 'user_id' => $user_id ] );

        $result = $wpdb->update(
            $table,
            [ 'is_default' => 1 ],
            [ 'address_id' => $address_id, 'user_id' => $user_id ]
        );

        return $result !== false;
    }

    /* ──────────────────────────────────────────────────────────────────────────
     * Validation
     * ────────────────────────────────────────────────────────────────────────── */

    /**
     * Validate address fields.
     *
     * @param  array $data
     * @return array  Associative array of field => error message. Empty = valid.
     */
    public static function validate_address( array $data ): array {
        $errors   = [];
        $required = [ 'first_name', 'last_name', 'address_1', 'city', 'country_code' ];

        foreach ( $required as $field ) {
            if ( empty( trim( $data[ $field ] ?? '' ) ) ) {
                /* translators: %s: field name */
                $errors[ $field ] = sprintf( __( '%s is required.', 'titancart-lite' ), ucwords( str_replace( '_', ' ', $field ) ) );
            }
        }

        // Validate country code
        if ( ! empty( $data['country_code'] ) ) {
            if ( strlen( $data['country_code'] ) !== 2 ) {
                $errors['country_code'] = __( 'Invalid country code.', 'titancart-lite' );
            } elseif ( ! self::country_exists( $data['country_code'] ) ) {
                $errors['country_code'] = __( 'Unknown country.', 'titancart-lite' );
            }
        }

        // State required for countries that have states configured
        if ( empty( $errors['country_code'] ) && ! empty( $data['country_code'] ) ) {
            if ( self::country_has_states( $data['country_code'] ) && empty( trim( $data['state_code'] ?? '' ) ) ) {
                $errors['state_code'] = __( 'State / province is required for this country.', 'titancart-lite' );
            }
        }

        return $errors;
    }

    /* ──────────────────────────────────────────────────────────────────────────
     * Countries & states
     * ────────────────────────────────────────────────────────────────────────── */

    /**
     * Get all active countries.
     *
     * @return array
     */
    public static function get_countries(): array {
        global $wpdb;
        $table = $wpdb->prefix . 'tc_countries';
        return $wpdb->get_results(
            "SELECT code, name FROM `{$table}` WHERE status = 'active' ORDER BY name ASC"
        ) ?: [];
    }

    /**
     * Get states/provinces for a country.
     *
     * @param  string $country_code ISO 3166-1 alpha-2
     * @return array
     */
    public static function get_states( string $country_code ): array {
        global $wpdb;
        $table = $wpdb->prefix . 'tc_states';
        return $wpdb->get_results( $wpdb->prepare(
            "SELECT id AS state_id, code, name FROM `{$table}` WHERE country_code = %s ORDER BY name ASC",
            strtoupper( $country_code )
        ) ) ?: [];
    }

    /**
     * True when a country exists in tc_countries.
     */
    public static function country_exists( string $country_code ): bool {
        global $wpdb;
        $table = $wpdb->prefix . 'tc_countries';
        return (bool) $wpdb->get_var( $wpdb->prepare(
            "SELECT code FROM `{$table}` WHERE code = %s AND status = 'active'",
            strtoupper( $country_code )
        ) );
    }

    /**
     * True when a country has state records in tc_states.
     */
    public static function country_has_states( string $country_code ): bool {
        global $wpdb;
        $table = $wpdb->prefix . 'tc_states';
        return (bool) $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM `{$table}` WHERE country_code = %s",
            strtoupper( $country_code )
        ) );
    }

    /* ──────────────────────────────────────────────────────────────────────────
     * Formatting
     * ────────────────────────────────────────────────────────────────────────── */

    /**
     * Format an address for display.
     *
     * @param  object|array $address
     * @param  string       $format  'html' | 'text'
     * @return string
     */
    public static function format_address( $address, string $format = 'html' ): string {
        if ( is_array( $address ) ) {
            $address = (object) $address;
        }

        $lines = array_filter( [
            trim( ( $address->first_name ?? '' ) . ' ' . ( $address->last_name ?? '' ) ),
            $address->company   ?? '',
            $address->address_1 ?? '',
            $address->address_2 ?? '',
            trim( ( $address->city ?? '' ) . ( ! empty( $address->state_code ) ? ', ' . $address->state_code : '' ) . ' ' . ( $address->zipcode ?? '' ) ),
            $address->country_code ?? '',
        ] );

        if ( $format === 'html' ) {
            return implode( '<br>', array_map( 'esc_html', $lines ) );
        }

        return implode( "\n", $lines );
    }
}
