<?php
namespace TCB\Admin;
use TCB\DB\PartnersDAO;
if ( ! defined( 'ABSPATH' ) ) { exit; }

/**
 * Media Partner credential provisioning.
 * Creates/links a WP user with the tc_partner role, stores encrypted raw password
 * for admin support, and sends a branded dashboard access email.
 */
class PartnerCredentialManager {
    private const CIPHER = 'aes-256-cbc';

    public static function generate_password( int $length = 16 ): string {
        $sets = [ 'ABCDEFGHJKLMNPQRSTUVWXYZ', 'abcdefghjkmnpqrstuvwxyz', '23456789', '!@#$%^&*-_=+' ];
        $all = implode( '', $sets );
        $pw = '';
        foreach ( $sets as $s ) { $pw .= $s[ random_int( 0, strlen( $s ) - 1 ) ]; }
        for ( $i = count( $sets ); $i < $length; $i++ ) { $pw .= $all[ random_int( 0, strlen( $all ) - 1 ) ]; }
        return str_shuffle( $pw );
    }

    public static function encrypt( string $plain ): string {
        $key = defined( 'TC_CREDENTIAL_KEY' ) ? TC_CREDENTIAL_KEY : 'FALLBACK_CHANGE_ME';
        $iv  = openssl_random_pseudo_bytes( openssl_cipher_iv_length( self::CIPHER ) );
        $enc = openssl_encrypt( $plain, self::CIPHER, $key, 0, $iv );
        return base64_encode( $iv . '::' . $enc );
    }

    public static function decrypt( string $encrypted ): string {
        $key = defined( 'TC_CREDENTIAL_KEY' ) ? TC_CREDENTIAL_KEY : 'FALLBACK_CHANGE_ME';
        $parts = explode( '::', base64_decode( $encrypted ), 2 );
        if ( count( $parts ) !== 2 ) return '';
        [ $iv, $enc ] = $parts;
        return openssl_decrypt( $enc, self::CIPHER, $key, 0, $iv ) ?: '';
    }


    public static function find_partner_for_login( string $identifier ): ?array {
        $identifier = trim( $identifier );
        if ( $identifier === '' ) { return null; }
        if ( is_email( $identifier ) ) {
            $partner = PartnersDAO::get_by_email( sanitize_email( $identifier ) );
            if ( $partner ) { return $partner; }
        }

        // Also allow username login by finding the WP user and then the linked partner.
        $user = get_user_by( 'login', sanitize_user( $identifier ) );
        if ( $user instanceof \WP_User ) {
            $partner = PartnersDAO::get_by_user_id( (int) $user->ID );
            if ( $partner ) { return $partner; }
        }
        return null;
    }

    /**
     * Dedicated partner authentication.
     * The partner table email + stored encrypted password are treated as the source of truth,
     * then the linked WP user is synced to that exact same password before logging in.
     * This fixes the bug where the admin screen showed a new password, but WordPress still
     * validated against an older linked user/password.
     */
    public static function authenticate_partner( string $identifier, string $password ): array {
        $partner = self::find_partner_for_login( $identifier );
        if ( ! $partner ) {
            return [ 'success' => false, 'message' => 'Invalid email or password.' ];
        }

        $stored = self::decrypt( (string) ( $partner['encrypted_password'] ?? '' ) );
        $stored_matches = $stored !== '' && hash_equals( $stored, $password );

        if ( ! $stored_matches ) {
            // Fallback only for old installs where the password was changed directly in WP.
            $login = is_email( $identifier ) ? sanitize_email( $identifier ) : sanitize_user( $identifier );
            $user = wp_authenticate( $login, $password );
            if ( is_wp_error( $user ) || ! ( $user instanceof \WP_User ) ) {
                return [ 'success' => false, 'message' => 'Invalid email or password.' ];
            }
            if ( self::is_protected_existing_user( $user ) || ! in_array( 'tc_partner', (array) $user->roles, true ) ) {
                return [ 'success' => false, 'message' => 'This account is not a media partner portal account.' ];
            }
            // If WP auth worked, sync the partner row to the working password for future validation.
            PartnersDAO::update( (int) $partner['id'], [ 'wp_user_id' => (int) $user->ID, 'email' => $user->user_email ] );
            PartnersDAO::store_encrypted_password( (int) $partner['id'], self::encrypt( $password ) );
            return [ 'success' => true, 'user' => new \WP_User( (int) $user->ID ), 'partner' => PartnersDAO::get_by_id( (int) $partner['id'] ) ?: $partner ];
        }

        $resolved = self::resolve_partner_user( $partner, $password );
        if ( empty( $resolved['success'] ) ) { return $resolved; }
        PartnersDAO::update( (int) $partner['id'], [ 'wp_user_id' => (int) $resolved['user_id'] ] );
        return [ 'success' => true, 'user' => new \WP_User( (int) $resolved['user_id'] ), 'partner' => PartnersDAO::get_by_id( (int) $partner['id'] ) ?: $partner ];
    }



    private static function is_protected_existing_user( \WP_User $user ): bool {
        // Protect real WordPress/admin accounts. Portal provisioning may only reuse low-privilege
        // accounts such as subscriber/customer, or existing pure tc_partner accounts.
        if ( user_can( $user, 'manage_options' ) || user_can( $user, 'edit_posts' ) || user_can( $user, 'edit_pages' ) || user_can( $user, 'delete_users' ) || user_can( $user, 'manage_woocommerce' ) ) {
            return true;
        }
        return false;
    }

    private static function is_dedicated_partner_user( \WP_User $user ): bool {
        $roles = (array) $user->roles;
        return in_array( 'tc_partner', $roles, true ) && count( array_diff( $roles, [ 'tc_partner' ] ) ) === 0;
    }

    private static function assert_safe_partner_user( \WP_User $user ): array {
        if ( self::is_protected_existing_user( $user ) ) {
            return [
                'success' => false,
                'message' => 'This email belongs to an admin/editor or protected WordPress account. It cannot be used as a media partner portal account. Please use a separate partner email.'
            ];
        }
        return [ 'success' => true ];
    }

    private static function unique_partner_login( array $partner, string $email ): string {
        $base = sanitize_user( sanitize_title( $partner['slug'] ?? $partner['name'] ?? current( explode( '@', $email ) ) ), true );
        if ( ! $base ) { $base = sanitize_user( current( explode( '@', $email ) ), true ); }
        if ( ! $base ) { $base = 'media-partner'; }
        $login = $base;
        $i = 1;
        while ( username_exists( $login ) ) {
            $login = $base . '-' . $i;
            $i++;
        }
        return $login;
    }

    private static function email_matches_user( \WP_User $user, string $email ): bool {
        return strtolower( (string) $user->user_email ) === strtolower( $email );
    }

    /**
     * Find or create the correct dedicated partner portal user.
     * Important: the partner email is the source of truth. A manually stored wp_user_id
     * is used only when it belongs to the same email. This prevents the exact bug where
     * a partner row points to a different WP user, the password is reset on that user,
     * but login is attempted with the partner email.
     */
    private static function resolve_partner_user( array $partner, string $plain_pw ): array {
        $email = sanitize_email( $partner['email'] ?? '' );
        if ( ! is_email( $email ) ) {
            return [ 'success' => false, 'message' => 'A valid partner email is required first.' ];
        }

        $linked_id = (int) ( $partner['wp_user_id'] ?? 0 );
        $linked_user = $linked_id ? get_user_by( 'id', $linked_id ) : false;

        // Use the linked user only if the linked user's email matches the partner email.
        if ( $linked_user instanceof \WP_User && self::email_matches_user( $linked_user, $email ) ) {
            $safe = self::assert_safe_partner_user( $linked_user );
            if ( ! $safe['success'] ) { return $safe; }
            wp_set_password( $plain_pw, (int) $linked_user->ID );
            $linked_user = new \WP_User( (int) $linked_user->ID );
            $linked_user->set_role( 'tc_partner' );
            update_user_meta( (int) $linked_user->ID, '_tcb_portal_account', 'partner' );
            return [ 'success' => true, 'user_id' => (int) $linked_user->ID, 'user' => $linked_user ];
        }

        // If the stored linked ID is wrong/mismatched, ignore it and use the partner email.
        $existing = get_user_by( 'email', $email );
        if ( $existing instanceof \WP_User ) {
            $safe = self::assert_safe_partner_user( $existing );
            if ( ! $safe['success'] ) { return $safe; }
            wp_set_password( $plain_pw, (int) $existing->ID );
            $existing = new \WP_User( (int) $existing->ID );
            $existing->set_role( 'tc_partner' );
            update_user_meta( (int) $existing->ID, '_tcb_portal_account', 'partner' );
            return [ 'success' => true, 'user_id' => (int) $existing->ID, 'user' => $existing ];
        }

        $user_id = wp_create_user( self::unique_partner_login( $partner, $email ), $plain_pw, $email );
        if ( is_wp_error( $user_id ) ) {
            return [ 'success' => false, 'message' => $user_id->get_error_message() ];
        }
        $user = new \WP_User( (int) $user_id );
        $user->set_role( 'tc_partner' );
        update_user_meta( (int) $user_id, '_tcb_portal_account', 'partner' );
        return [ 'success' => true, 'user_id' => (int) $user_id, 'user' => $user ];
    }

    public static function provision_partner( int $partner_id ): array {
        $partner = PartnersDAO::get_by_id( $partner_id );
        if ( ! $partner ) return [ 'success' => false, 'message' => 'Media partner not found.' ];
        if ( empty( $partner['email'] ) || ! is_email( $partner['email'] ) ) return [ 'success' => false, 'message' => 'A valid partner email is required first.' ];

        $plain_pw = self::generate_password();
        $email = sanitize_email( $partner['email'] );

        $resolved = self::resolve_partner_user( $partner, $plain_pw );
        if ( empty( $resolved['success'] ) ) { return $resolved; }

        $user_id = (int) $resolved['user_id'];
        wp_update_user( [
            'ID'           => $user_id,
            'user_email'   => $email,
            'display_name' => sanitize_text_field( $partner['name'] ?: $email ),
        ] );

        PartnersDAO::update( $partner_id, [ 'wp_user_id' => $user_id ] );
        PartnersDAO::store_encrypted_password( $partner_id, self::encrypt( $plain_pw ) );
        $partner['wp_user_id'] = $user_id;
        return [ 'success' => true, 'partner' => $partner, 'plain_pw' => $plain_pw, 'user_id' => $user_id ];
    }

    private static function notification_cc_emails( string $to = '' ): array {
        $raw = (string) get_option( 'tcb_email_cc_recipients', 'fmushtaq@gmail.com' );
        $raw = apply_filters( 'tc_notification_cc_emails', $raw, $to );
        $to_email = strtolower( sanitize_email( $to ) );
        $emails = [];

        foreach ( preg_split( '/[,;\s]+/', (string) $raw ) as $candidate ) {
            $email = sanitize_email( trim( (string) $candidate ) );
            $key = strtolower( $email );
            if ( ! $email || ! is_email( $email ) || $key === $to_email || isset( $emails[ $key ] ) ) {
                continue;
            }
            $emails[ $key ] = $email;
        }

        return array_values( $emails );
    }

    private static function custom_from_email(): string {
        if ( get_option( 'tcb_email_use_default_sender', 'yes' ) === 'yes' ) {
            return '';
        }
        $email = sanitize_email( (string) get_option( 'tcb_email_custom_sender', '' ) );
        return is_email( $email ) ? $email : '';
    }

    public static function custom_mail_from( string $from ): string {
        $custom = self::custom_from_email();
        return $custom ?: $from;
    }

    private static function mail_headers( string $to = '' ): array {
        $headers = [ 'Content-Type: text/html; charset=UTF-8' ];
        foreach ( self::notification_cc_emails( $to ) as $cc_email ) {
            $headers[] = 'Cc: ' . $cc_email;
        }
        return $headers;
    }


    private static function send_mail( string $to, string $subject, string $body ): bool {
        if ( ! is_email( $to ) ) { return false; }
        $subject = wp_strip_all_tags( $subject );
        $custom_from = self::custom_from_email();
        add_filter( 'wp_mail_content_type', [ self::class, 'html_content_type' ] );
        if ( $custom_from ) {
            add_filter( 'wp_mail_from', [ self::class, 'custom_mail_from' ] );
        }
        $sent = wp_mail( $to, $subject, $body, self::mail_headers( $to ) );
        if ( ! $sent ) { update_option( 'tc_last_mail_error', 'wp_mail returned false for ' . sanitize_email( $to ) . ' at ' . current_time( 'mysql' ), false ); }
        if ( $custom_from ) {
            remove_filter( 'wp_mail_from', [ self::class, 'custom_mail_from' ] );
        }
        remove_filter( 'wp_mail_content_type', [ self::class, 'html_content_type' ] );
        return (bool) $sent;
    }
    public static function html_content_type(): string { return 'text/html'; }
    private static function replace_tokens( string $text, array $tokens ): string {
        foreach ( $tokens as $key => $value ) { $text = str_replace( '{' . $key . '}', (string) $value, $text ); }
        return $text;
    }

    public static function default_partner_credentials_body_text(): string {
        return "Your TechCon Media Partner Dashboard access has been created for the TechCon media interview booking system.\n\nThis dashboard is used to review incoming speaker interview requests, check speaker details, approve or decline requests, and manage scheduled interview slots.\n\nHow it works:\n1. Sign in through the Partner Login link below.\n2. Review pending interview requests in your dashboard.\n3. Approve a request to confirm the booking, or decline it if the slot is not suitable.\n4. After approval, use the dashboard to view booking details and communicate with the speaker when available.\n\nPlease keep these credentials secure and use this account only for your TechCon media partner booking access.";
    }

    private static function body_text_to_html( string $text ): string {
        $text = trim( $text );
        if ( $text === '' ) {
            $text = self::default_partner_credentials_body_text();
        }
        return wpautop( wp_kses_post( $text ) );
    }

    private static function email_text_option( string $option, string $default ): string {
        $value = trim( (string) get_option( $option, $default ) );
        return $value !== '' ? $value : $default;
    }

    private static function email_url_option( string $option, string $default ): string {
        $value = trim( (string) get_option( $option, $default ) );
        return $value !== '' ? $value : $default;
    }

    public static function send_credentials( int $partner_id ): bool {
        $result = self::provision_partner( $partner_id );
        if ( ! $result['success'] ) return false;
        $partner = $result['partner'];
        $pw = $result['plain_pw'];
        $login = home_url( '/partner-login/' );
        $dashboard = home_url( '/partner-dashboard/' );
        $tokens = [ 'site_name' => wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), 'partner_name' => $partner['name'], 'partner_email' => $partner['email'], 'email' => $partner['email'], 'username' => ( $user = get_user_by( 'id', (int) $result['user_id'] ) ) ? $user->user_login : $partner['email'], 'password' => $pw, 'login_url' => $login, 'dashboard_url' => $dashboard ];
        $subject = self::replace_tokens( get_option( 'tcb_email_partner_credentials_subject', 'Your TechCon Media Partner Dashboard Access - {site_name}' ), $tokens );
        $body_text = self::replace_tokens( (string) get_option( 'tcb_email_partner_credentials_body_text', self::default_partner_credentials_body_text() ), $tokens );
        $body = self::email_html( $partner['name'], $partner['email'], $pw, $login, $dashboard, $body_text );
        $sent = self::send_mail( $partner['email'], $subject, $body );
        if ( $sent ) PartnersDAO::mark_credentials_sent( $partner_id );
        return $sent;
    }


    public static function set_custom_password( int $partner_id, string $new_pw ): bool {
        $partner = PartnersDAO::get_by_id( $partner_id );
        if ( ! $partner || empty( $partner['email'] ) || ! is_email( $partner['email'] ) ) {
            return false;
        }
        $resolved = self::resolve_partner_user( $partner, $new_pw );
        if ( empty( $resolved['success'] ) || empty( $resolved['user_id'] ) ) {
            return false;
        }
        PartnersDAO::update( $partner_id, [ 'wp_user_id' => (int) $resolved['user_id'] ] );
        PartnersDAO::store_encrypted_password( $partner_id, self::encrypt( $new_pw ) );
        return true;
    }
    public static function get_plain_password( int $partner_id ): string {
        $partner = PartnersDAO::get_by_id( $partner_id );
        if ( ! $partner || empty( $partner['encrypted_password'] ) ) return '';
        return self::decrypt( $partner['encrypted_password'] );
    }

    private static function email_html( string $name, string $email, string $pw, string $login, string $dashboard, string $body_text = '' ): string {
        $site = esc_html( get_bloginfo( 'name' ) );
        $safe_name = esc_html( $name );
        $safe_email = esc_html( $email );
        $safe_pw = esc_html( $pw );
        $safe_login = esc_url( $login );
        $safe_dashboard = esc_url( $dashboard );
        $dashboard_display = esc_html( rtrim( preg_replace( '#^https?://#i', '', $dashboard ), '/' ) );
        $header_text = esc_html( self::email_text_option( 'tcb_partner_credential_header_text', 'Media Partner Dashboard Access' ) );
        $button_text = esc_html( self::email_text_option( 'tcb_partner_credential_button_text', 'Sign In to Partner Dashboard →' ) );
        $direct_link_label = esc_html( self::email_text_option( 'tcb_partner_credential_direct_link_label', 'Dashboard direct link:' ) );
        $footer_techcon_text = esc_html( self::email_text_option( 'tcb_credential_footer_techcon_text', 'TechCon Global' ) );
        $footer_powered_by_text = esc_html( self::email_text_option( 'tcb_credential_footer_powered_by_text', 'Powered by' ) );
        $footer_brilzen_text = esc_html( self::email_text_option( 'tcb_credential_footer_brilzen_text', 'BRILZEN (SMC-PRIVATE) LIMITED' ) );
        $techcon_url = esc_url( self::email_url_option( 'tcb_credential_footer_techcon_url', 'https://techconglobal.com/' ) );
        $brilzen_url = esc_url( self::email_url_option( 'tcb_credential_footer_brilzen_url', 'https://brilzen.com/' ) );
        $message_html = self::body_text_to_html( $body_text );
        return <<<HTML
<!DOCTYPE html><html><head><meta charset="UTF-8">
<style>body{margin:0;padding:0;background:#070712;font-family:Arial,sans-serif}.wrap{max-width:580px;margin:40px auto;background:#13082a;border:1px solid rgba(230,26,132,.28);border-radius:18px;overflow:hidden}.hd{background:linear-gradient(135deg,#e61a84,#4e24c0);padding:32px;text-align:center}.hd h1{margin:0;color:#fff;font-size:22px}.bd{padding:32px}.bd p{color:#c8d4e8;font-size:14px;line-height:1.65}.cred{background:rgba(255,255,255,.06);border:1px solid rgba(230,26,132,.3);border-radius:12px;padding:16px 20px;margin:20px 0}.cred-row{margin-bottom:8px}.cred-row:last-child{margin-bottom:0}.cred-label{display:inline-block;color:rgba(255,255,255,.45);font-size:11px;font-weight:700;text-transform:uppercase;min-width:92px}.cred-val{color:#fff;font-size:15px;font-family:monospace;font-weight:700}.btn{display:block;text-align:center;padding:14px 28px;background:linear-gradient(135deg,#e61a84,#4e24c0);color:#fff!important;text-decoration:none;border-radius:30px;font-weight:700;font-size:14px;margin:24px 0 8px}.ft{padding:18px 32px;border-top:1px solid rgba(255,255,255,.08);text-align:center;color:rgba(255,255,255,.35);font-size:11px}.ft a{color:#e61a84;text-decoration:none}</style>
</head><body><div class="wrap"><div class="hd"><h1>⚡ {$site}</h1><p style="color:#fff;margin:8px 0 0;opacity:.75;">{$header_text}</p></div><div class="bd"><p>Hi <strong style="color:#fff;">{$safe_name}</strong>,</p>{$message_html}<div class="cred"><div class="cred-row"><span class="cred-label">Email Login</span><span class="cred-val">{$safe_email}</span></div><div class="cred-row"><span class="cred-label">Password</span><span class="cred-val">{$safe_pw}</span></div></div><a href="{$safe_login}" class="btn">{$button_text}</a><p style="font-size:12px;color:rgba(255,255,255,.45);">{$direct_link_label} <a href="{$safe_dashboard}" style="color:#0d8bff;text-decoration:underline;">{$dashboard_display}</a></p></div><div class="ft">&copy; <a href="{$techcon_url}">{$footer_techcon_text}</a> · {$footer_powered_by_text} <a href="{$brilzen_url}">{$footer_brilzen_text}</a></div></div></body></html>
HTML;
    }
}
