<?php

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

class AIEO_SSO_Provider_Apple extends AIEO_SSO_Provider_Base {

    const AUTH_URL  = 'https://appleid.apple.com/auth/authorize';
    const TOKEN_URL = 'https://appleid.apple.com/auth/token';

    public function code()  { return 'apple'; }
    public function label() { return 'Apple'; }

    public function auth_url($state, $redirect_uri) {
        $cfg = $this->credentials();
        if ($cfg['client_id'] === '') {
            return new WP_Error('sso_no_client_id', 'Apple service_id missing.');
        }
        return self::AUTH_URL . '?' . http_build_query(array(
            'response_type' => 'code',
            'response_mode' => 'form_post', 
            'client_id'     => $cfg['client_id'],
            'redirect_uri'  => $redirect_uri,
            'scope'         => 'name email',
            'state'         => $state,
        ));
    }

    public function exchange_token($code, $redirect_uri) {
        $cfg = $this->credentials();
        $client_secret = $this->build_client_secret_jwt($cfg);
        if (is_wp_error($client_secret)) { return $client_secret; }

        return $this->http_post_form(self::TOKEN_URL, array(
            'grant_type'    => 'authorization_code',
            'code'          => $code,
            'redirect_uri'  => $redirect_uri,
            'client_id'     => $cfg['client_id'],
            'client_secret' => $client_secret,
        ));
    }

    public function fetch_profile(array $token) {
        $id_token = (string) ($token['id_token'] ?? '');
        if ($id_token === '') { return new WP_Error('sso_no_id_token', 'Apple did not return an id_token.'); }
        $claims = $this->decode_id_token($id_token);
        if (is_wp_error($claims)) { return $claims; }

        
        
        
        
        $first = ''; $last = '';
        $user_post = isset($_POST['user']) ? (string) wp_unslash($_POST['user']) : '';
        if ($user_post !== '') {
            $u = json_decode($user_post, true);
            if (is_array($u) && isset($u['name']) && is_array($u['name'])) {
                $first = (string) ($u['name']['firstName'] ?? '');
                $last  = (string) ($u['name']['lastName']  ?? '');
            }
        }

        return array(
            'provider_uid' => (string) ($claims['sub']   ?? ''),
            'email'        => (string) ($claims['email'] ?? ''),
            'first_name'   => $first,
            'last_name'    => $last,
            'avatar'       => '', 
            'raw'          => $claims,
        );
    }

    
    private function build_client_secret_jwt(array $cfg) {
        $secret_blob = (string) $cfg['client_secret'];
        $bag = json_decode($secret_blob, true);
        if (!is_array($bag) || empty($bag['team_id']) || empty($bag['key_id']) || empty($bag['p8'])) {
            return new WP_Error('sso_apple_secret', 'Apple client_secret must be JSON: {team_id, key_id, p8}.');
        }
        if (!function_exists('openssl_sign')) {
            return new WP_Error('sso_no_openssl', 'OpenSSL extension required for Apple Sign-In.');
        }

        $now = time();
        $header = array('alg' => 'ES256', 'kid' => $bag['key_id'], 'typ' => 'JWT');
        $claims = array(
            'iss' => $bag['team_id'],
            'iat' => $now,
            'exp' => $now + 600, 
            'aud' => 'https://appleid.apple.com',
            'sub' => $cfg['client_id'],
        );
        $signing_input = $this->b64u(wp_json_encode($header)) . '.' . $this->b64u(wp_json_encode($claims));

        $key = openssl_pkey_get_private((string) $bag['p8']);
        if (!$key) { return new WP_Error('sso_apple_key', 'Could not load Apple .p8 private key.'); }
        $sig = '';
        $ok = openssl_sign($signing_input, $sig, $key, 'sha256');
        if (!$ok) { return new WP_Error('sso_apple_sign', 'openssl_sign failed.'); }

        
        $sig = $this->der_to_raw_es256($sig);
        if ($sig === false) { return new WP_Error('sso_apple_sig_format', 'Could not convert ES256 signature.'); }

        return $signing_input . '.' . $this->b64u($sig);
    }

    private function decode_id_token($jwt) {
        $parts = explode('.', $jwt);
        if (count($parts) !== 3) { return new WP_Error('sso_jwt_format', 'Malformed id_token.'); }
        $payload = json_decode($this->b64u_decode($parts[1]), true);
        if (!is_array($payload)) { return new WP_Error('sso_jwt_payload', 'Could not decode id_token payload.'); }
        
        
        
        return $payload;
    }

    private function b64u($s) {
        return rtrim(strtr(base64_encode((string) $s), '+/', '-_'), '=');
    }
    private function b64u_decode($s) {
        $s = strtr((string) $s, '-_', '+/');
        $pad = strlen($s) % 4;
        if ($pad) { $s .= str_repeat('=', 4 - $pad); }
        return base64_decode($s);
    }

    
    private function der_to_raw_es256($der) {
        $offset = 0;
        if (ord($der[$offset++]) !== 0x30) { return false; } 
        
        $len = ord($der[$offset++]);
        if ($len & 0x80) {
            $n = $len & 0x7F;
            $len = 0;
            for ($i = 0; $i < $n; $i++) { $len = ($len << 8) | ord($der[$offset++]); }
        }
        $extract = function (&$o) use ($der) {
            if (ord($der[$o++]) !== 0x02) { return false; } 
            $l = ord($der[$o++]);
            if ($l & 0x80) {
                $n = $l & 0x7F;
                $l = 0;
                for ($i = 0; $i < $n; $i++) { $l = ($l << 8) | ord($der[$o++]); }
            }
            $b = substr($der, $o, $l); $o += $l;
            
            while (strlen($b) > 32 && $b[0] === "\x00") { $b = substr($b, 1); }
            $b = str_pad($b, 32, "\x00", STR_PAD_LEFT);
            return $b;
        };
        $r = $extract($offset); if ($r === false) return false;
        $s = $extract($offset); if ($s === false) return false;
        return $r . $s;
    }
}

add_filter('aieo_sso_providers', static function (array $providers) {
    if (!isset($providers['apple']) && class_exists('AIEO_SSO_Provider_Apple')) {
        $providers['apple'] = new AIEO_SSO_Provider_Apple();
    }
    return $providers;
});
