<?php

declare(strict_types=1);

namespace SiteFort\Modules\Firewall\GeoIP;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Country-level access control.
 *
 * Blocks or allows requests based on the visitor's country, as determined
 * by the GeoIPService. Supports two operational modes:
 *
 * - `block_list`: Block requests from countries in the configured list.
 * - `allow_list`: Block requests from countries NOT in the configured list,
 *   including requests where the country cannot be determined.
 *
 */
class CountryBlocker {

    /**
     * Firewall settings array.
     *
     * @var array
     */
    private array $settings;

    /**
     * GeoIP lookup service instance.
     *
     * @var GeoIPService
     */
    private GeoIPService $geoip;

    /**
     * @param array        $settings Firewall settings containing country blocking configuration.
     * @param GeoIPService $geoip    GeoIP service for country lookups.
     */
    public function __construct( array $settings, GeoIPService $geoip ) {
        $this->settings = $settings;
        $this->geoip    = $geoip;
    }

    /**
     * Determine whether a request from the given IP should be blocked.
     *
     * Returns an associative array with the blocking decision:
     * - `blocked`:      (bool)   Whether the request should be blocked.
     * - `country_code`: (string) The detected ISO 3166-1 alpha-2 country code, or empty string.
     * - `lookup_performed`: (bool) Whether a geo lookup was actually attempted.
     * - `reason`:       (string) Human-readable reason for the decision.
     *
     * @param string $ip The visitor's IP address.
     * @return array{blocked: bool, country_code: string, lookup_performed: bool, reason: string}
     */
    public function should_block( string $ip ): array {
        $default = [
            'blocked'      => false,
            'country_code' => '',
            'lookup_performed' => false,
            'reason'       => '',
        ];

        // Logged-in administrators should never be geo-blocked from their own site.
        if ( \SiteFort\Includes\Capabilities::can_manage_plugin() ) {
            return $default;
        }

        // Check if country blocking is enabled.
        if ( empty( $this->settings['geoip_enabled'] ) ) {
            return $default;
        }

        $mode      = $this->settings['geoip_mode'] ?? 'block_list';
        $countries = $this->get_country_list();

        if ( empty( $countries ) ) {
            return $default;
        }

        $origin_provider_ready = $this->geoip->has_origin_country_provider();

        // Look up the country.
        $geo = $this->geoip->lookup( $ip );

        if ( $geo === null ) {
            if ( ! $origin_provider_ready ) {
                return [
                    'blocked'      => false,
                    'country_code' => '',
                    'lookup_performed' => false,
                    'reason'       => '',
                ];
            }

            if ( $mode === 'allow_list' ) {
                return [
                    'blocked'      => true,
                    'country_code' => '',
                    'lookup_performed' => true,
                    'reason'       => 'Country could not be determined and allow-list mode requires a known allowed country.',
                ];
            }

            return [
                'blocked'      => false,
                'country_code' => '',
                'lookup_performed' => true,
                'reason'       => '',
            ];
        }

        $country_code = strtoupper( $geo['country_code'] );
        $in_list = in_array( $country_code, $countries, true );

        if ( $mode === 'block_list' ) {
            // Block if the country IS in the list.
            if ( $in_list ) {
                return [
                    'blocked'      => true,
                    'country_code' => $country_code,
                    'lookup_performed' => true,
                    'reason'       => 'Country ' . $country_code . ' is on the block list.',
                ];
            }
        } elseif ( $mode === 'allow_list' ) {
            // Block if the country is NOT in the list.
            if ( ! $in_list ) {
                return [
                    'blocked'      => true,
                    'country_code' => $country_code,
                    'lookup_performed' => true,
                    'reason'       => 'Country ' . $country_code . ' is not on the allow list.',
                ];
            }
        }

        return [
            'blocked'      => false,
            'country_code' => $country_code,
            'lookup_performed' => true,
            'reason'       => '',
        ];
    }

    /**
     * Parse the configured country list into an array of uppercase ISO codes.
     *
     * The list can be stored as a comma-separated string or a newline-separated
     * textarea value. Both formats are supported.
     *
     * @return string[] Array of uppercase ISO 3166-1 alpha-2 country codes.
     */
    private function get_country_list(): array {
        $raw = $this->settings['geoip_countries'] ?? '';

        if ( is_array( $raw ) ) {
            return array_map( 'strtoupper', array_map( 'trim', $raw ) );
        }

        if ( ! is_string( $raw ) || $raw === '' ) {
            return [];
        }

        // Support both comma-separated and newline-separated formats.
        $items = preg_split( '/[\s,]+/', $raw, -1, PREG_SPLIT_NO_EMPTY );
        if ( $items === false ) {
            return [];
        }

        return array_map( 'strtoupper', array_map( 'trim', $items ) );
    }
}
