<?php

declare(strict_types=1);

namespace BadamSoft\WooProductExporter\Exporter\Language;

use BadamSoft\WooProductExporter\Core\Plugin;

/**
 * Detects available languages from WPML/Polylang or WordPress defaults.
 *
 * @internal This helper centralises multilingual detection logic so the exporter can
 *           operate in a single place without reaching directly into integration APIs.
 */
class LanguageDetector {
    private Plugin $plugin;

    public function __construct( Plugin $plugin ) {
        $this->plugin = $plugin;
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function detect(): array {
        $languages = [];

        if ( function_exists( 'pll_languages_list' ) ) {
            $languages = $this->detect_from_polylang();
        } elseif ( function_exists( 'apply_filters' ) && has_filter( 'wpml_active_languages' ) ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
            $languages = $this->detect_from_wpml();
        }

        if ( empty( $languages ) ) {
            $languages = [ $this->build_from_locale( $this->get_default_locale(), 'wordpress', true ) ];
        } else {
            $languages = $this->ensure_primary_locale_present( $languages );
        }

        $languages = $this->deduplicate_by_locale( $languages );

        usort(
            $languages,
            static function ( array $a, array $b ): int {
                if ( ! empty( $a['is_default'] ) && empty( $b['is_default'] ) ) {
                    return -1;
                }

                if ( empty( $a['is_default'] ) && ! empty( $b['is_default'] ) ) {
                    return 1;
                }

                return strcasecmp( (string) ( $a['name'] ?? $a['locale'] ?? '' ), (string) ( $b['name'] ?? $b['locale'] ?? '' ) );
            }
        );

        return array_values( $languages );
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function detect_from_polylang(): array {
        $items = pll_languages_list( [ 'fields' => 'all' ] );

        if ( ! is_array( $items ) ) {
            return [];
        }

        $languages = [];

        foreach ( $items as $item ) {
            if ( ! is_array( $item ) ) {
                continue;
            }

            $slug   = isset( $item['slug'] ) ? (string) $item['slug'] : '';
            $locale = isset( $item['locale'] ) ? (string) $item['locale'] : '';

            if ( '' === $slug && '' === $locale ) {
                continue;
            }

            if ( '' === $locale && function_exists( 'pll_get_locale' ) ) {
                $locale = (string) pll_get_locale( $slug );
            }

            if ( '' === $locale ) {
                $locale = $this->normalize_locale_from_code( $slug );
            }

            $languages[] = [
                'code'        => $slug ?: $this->normalize_code_from_locale( $locale ),
                'locale'      => $locale,
                'name'        => isset( $item['name'] ) ? (string) $item['name'] : $locale,
                'native_name' => isset( $item['native_name'] ) ? (string) $item['native_name'] : ( isset( $item['name'] ) ? (string) $item['name'] : $locale ),
                'is_default'  => ! empty( $item['is_default'] ),
                'source'      => 'polylang',
                'query_code'  => $slug ?: $this->normalize_code_from_locale( $locale ),
            ];
        }

        return $languages;
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function detect_from_wpml(): array {
        $active_languages = apply_filters( 'wpml_active_languages', null, [ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
            'skip_missing' => 0,
            'orderby'      => 'id',
            'order'        => 'ASC',
        ] );

        if ( empty( $active_languages ) || ! is_array( $active_languages ) ) {
            return [];
        }

        $default_language = apply_filters( 'wpml_default_language', null ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
        $languages        = [];

        foreach ( $active_languages as $language ) {
            if ( ! is_array( $language ) ) {
                continue;
            }

            $code   = isset( $language['language_code'] ) ? (string) $language['language_code'] : '';
            $locale = isset( $language['default_locale'] ) ? (string) $language['default_locale'] : '';

            if ( '' === $code && '' === $locale ) {
                continue;
            }

            if ( '' === $locale ) {
                $locale = $this->normalize_locale_from_code( $code );
            }

            $name = $language['translated_name'] ?? $language['english_name'] ?? $locale;
            $native_name = $language['native_name'] ?? $name;

            $languages[] = [
                'code'        => $code ?: $this->normalize_code_from_locale( $locale ),
                'locale'      => $locale,
                'name'        => (string) $name,
                'native_name' => (string) $native_name,
                'is_default'  => ( $default_language && $code === $default_language ),
                'source'      => 'wpml',
                'query_code'  => $code ?: $this->normalize_code_from_locale( $locale ),
            ];
        }

        return $languages;
    }

    private function ensure_primary_locale_present( array $languages ): array {
        $primary_locale = $this->get_default_locale();

        if ( '' === $primary_locale ) {
            return $languages;
        }

        foreach ( $languages as &$language ) {
            if ( isset( $language['locale'] ) && $primary_locale === $language['locale'] ) {
                $language['is_default'] = true;

                return $languages;
            }
        }
        unset( $language );

        $languages[] = $this->build_from_locale( $primary_locale, 'wordpress', true );

        return $languages;
    }

    private function deduplicate_by_locale( array $languages ): array {
        $unique = [];

        foreach ( $languages as $language ) {
            $locale = isset( $language['locale'] ) ? (string) $language['locale'] : '';

            if ( '' === $locale || isset( $unique[ $locale ] ) ) {
                continue;
            }

            $unique[ $locale ] = $language;
        }

        return array_values( $unique );
    }

    private function build_from_locale( string $locale, string $source, bool $is_default ): array {
        $code = $this->normalize_code_from_locale( $locale );

        return [
            'code'        => $code,
            'locale'      => $locale,
            'name'        => $locale,
            'native_name' => $locale,
            'is_default'  => $is_default,
            'source'      => $source,
            'query_code'  => $code,
        ];
    }

    private function get_default_locale(): string {
        if ( function_exists( 'determine_locale' ) ) {
            return (string) determine_locale();
        }

        $user_locale = function_exists( 'get_user_locale' ) ? (string) get_user_locale() : '';

        if ( '' !== $user_locale ) {
            return $user_locale;
        }

        return (string) get_locale();
    }

    private function normalize_code_from_locale( string $locale ): string {
        if ( '' === $locale ) {
            return 'default';
        }

        $parts = preg_split( '/[_-]/', $locale );
        $code  = isset( $parts[0] ) ? strtolower( (string) $parts[0] ) : strtolower( $locale );

        return (string) preg_replace( '/[^a-z0-9]/', '', $code ) ?: 'default';
    }

    private function normalize_locale_from_code( string $code ): string {
        $code = trim( $code );

        if ( '' === $code ) {
            return 'en_US';
        }

        $code = strtolower( $code );

        switch ( $code ) {
            case 'en':
                return 'en_US';
            case 'ru':
                return 'ru_RU';
            case 'es':
                return 'es_ES';
            case 'fr':
                return 'fr_FR';
            case 'de':
                return 'de_DE';
            case 'it':
                return 'it_IT';
            case 'tr':
                return 'tr_TR';
            case 'ar':
                return 'ar_AE';
            case 'he':
                return 'he_IL';
            case 'hi':
                return 'hi_IN';
            case 'zh':
                return 'zh_CN';
            default:
                return strtoupper( $code ) . '_' . strtoupper( $code );
        }
    }
}
