using Blazored.LocalStorage;
using CMSMicroservice.Protobuf.Protos.User;
using FrontOffice.Main.Utilities;
using Grpc.Core;
using Microsoft.AspNetCore.Components;
using MudBlazor;
using MetadataAlias = Grpc.Core.Metadata; // resolve ambiguity with MudBlazor.Metadata

namespace FrontOffice.Main.Shared;

public partial class AuthDialog : IDisposable
{
    [Parameter] public bool HideCancelButton { get; set; }
    // [Parameter] public bool EnableCaptcha { get; set; }
    [Parameter] public bool InlineMode { get; set; }

    public enum AuthStep { Phone, Verify }
    private const int DefaultResendCooldown = 120;
    public const int MaxVerificationAttempts = 5;
    private const string PhoneStorageKey = "auth:phone-number";
    private const string RedirectStorageKey = "auth:redirect";
    private const string TokenStorageKey = "auth:token";
    private const string OtpPurpose = "Login";

    public AuthStep _currentStep = AuthStep.Phone;

    private readonly CreateNewOtpTokenRequest _phoneRequest = new();
    private PhoneVerifyForm? _phoneVerifyForm;

    private readonly VerifyOtpTokenRequest _verifyRequest = new();

    private bool _isBusy;
    private string? _phoneNumber;
    private string? _errorMessage;
    private string? _infoMessage;
    private Timer? _resendTimer;
    private int _resendRemaining;
    private int _attemptsLeft = MaxVerificationAttempts;
    private CancellationTokenSource? _operationCts;

    // Captcha fields
    private string? _captchaCode;
    private string? _captchaInput;

    // Referral code field
    private string? _referralCode;

    [Inject] private ILocalStorageService LocalStorage { get; set; } = default!;
    [Inject] private UserContract.UserContractClient UserClient { get; set; } = default!;

    [CascadingParameter]
    private IMudDialogInstance MudDialog { get; set; }

    [Parameter] public EventCallback OnLoginSuccess { get; set; }

    private bool IsVerificationLocked => _attemptsLeft <= 0;

    protected override async Task OnInitializedAsync()
    {
        _phoneRequest.Purpose = OtpPurpose;
        _verifyRequest.Purpose = OtpPurpose;

        // if (EnableCaptcha)
        // {
            GenerateCaptcha();
        // }
        var storedPhone = await LocalStorage.GetItemAsync<string>(PhoneStorageKey);
        if (!string.IsNullOrWhiteSpace(storedPhone))
        {
            _phoneRequest.Mobile = storedPhone;
        }

        _referralCode = await LocalStorage.GetItemAsync<string>("referral:code");
        // await LocalStorage.RemoveItemAsync(TokenStorageKey);
    }

    private async Task OnReferralCodeChanged(string? value)
    {
        _referralCode = value;
        if (!string.IsNullOrWhiteSpace(value))
            await LocalStorage.SetItemAsync("referral:code", value);
        else
            await LocalStorage.RemoveItemAsync("referral:code");
    }

    private void GenerateCaptcha()
    {
        _captchaCode = Guid.NewGuid().ToString("N")[..6].ToUpperInvariant();
        _captchaInput = string.Empty;
    }

    public async Task SendOtpAsync()
    {
        _errorMessage = null;
        var phoneForm = _phoneVerifyForm?.GetPhoneForm();
        if (phoneForm is null)
            return;

        await phoneForm.Validate();
        if (!phoneForm.IsValid)
            return;

        // if (EnableCaptcha)
        // {
            if (string.IsNullOrWhiteSpace(_captchaInput) || !string.Equals(_captchaInput.Trim(), _captchaCode, StringComparison.OrdinalIgnoreCase))
            {
                _errorMessage = "کد کپچا صحیح نیست.";
                return;
            }
        // }

        _isBusy = true;
        _operationCts?.Cancel();
        _operationCts?.Dispose();
        _operationCts = new CancellationTokenSource();

        try
        {
            var validationResult = true; // _phoneRequestValidator.Validate(_phoneRequest);
            if (!validationResult)
            {
                _errorMessage = "لطفاً شماره تلفن معتبر وارد کنید."; // string.Join(" ", validationResult.Errors.Select(e => e.ErrorMessage).Distinct());
                return;
            }

            var metadata = await BuildAuthMetadataAsync();
            CreateNewOtpTokenResponse response = metadata is not null
                ? await UserClient.CreateNewOtpTokenAsync(_phoneRequest, metadata, cancellationToken: _operationCts.Token)
                : await UserClient.CreateNewOtpTokenAsync(_phoneRequest, cancellationToken: _operationCts.Token);

            if (response?.Success != true)
            {
                _errorMessage = string.IsNullOrWhiteSpace(response?.Message)
                    ? "ارسال رمز پویا با خطا مواجه شد. لطفاً دوباره تلاش کنید."
                    : response!.Message;
                return;
            }

            await LocalStorage.SetItemAsync(PhoneStorageKey, _phoneRequest.Mobile);
            _phoneNumber = _phoneRequest.Mobile;
            _currentStep = AuthStep.Verify;
            StartResendCountdown();
        }
        catch (RpcException rpcEx)
        {
            _errorMessage = !string.IsNullOrWhiteSpace(rpcEx.Status.Detail)
                ? rpcEx.Status.Detail
                : "ارسال رمز پویا با خطا مواجه شد. لطفاً دوباره تلاش کنید.";
        }
        catch (OperationCanceledException)
        {
            // ignored - user canceled operation
        }
        catch (Exception ex)
        {
            _errorMessage = ex.Message;
        }
        finally
        {
            _isBusy = false;
            ClearOperationToken();
            await InvokeAsync(StateHasChanged);
        }
    }

    public async Task<bool> VerifyOtpAsync()
    {
        _errorMessage = null;
        _infoMessage = null;

        var verifyForm = _phoneVerifyForm?.GetVerifyForm();
        if (verifyForm is null)
            return false;

        await verifyForm.Validate();
        if (!verifyForm.IsValid)
            return false;

        if (IsVerificationLocked)
        {
            _errorMessage = "تعداد تلاش‌های مجاز به پایان رسیده است. لطفاً رمز جدید دریافت کنید.";
            return false;
        }

        if (string.IsNullOrWhiteSpace(_phoneNumber))
        {
            _errorMessage = "شماره موبایل یافت نشد. لطفاً دوباره تلاش کنید.";
            return false;
        }

        _isBusy = true;
        var cancellationToken = PrepareOperationToken();

        try
        {
            _verifyRequest.Mobile = _phoneNumber;

            var storedReferralCode = await LocalStorage.GetItemAsync<string>("referral:code");
            var effectiveReferralCode = !string.IsNullOrWhiteSpace(_referralCode)
                ? _referralCode
                : storedReferralCode;
            if (!string.IsNullOrWhiteSpace(effectiveReferralCode))
            {
                _verifyRequest.ParentReferralCode = effectiveReferralCode;
            }

            var validationResult = true; // _verifyRequestValidator.Validate(_verifyRequest);
            if (!validationResult)
            {
                _errorMessage = "لطفاً کد تأیید معتبر وارد کنید."; // string.Join(" ", validationResult.Errors.Select(e => e.ErrorMessage).Distinct());
                return false;
            }

            var metadata = await BuildAuthMetadataAsync();
            VerifyOtpTokenResponse response = metadata is not null
                ? await UserClient.VerifyOtpTokenAsync(_verifyRequest, metadata, cancellationToken: cancellationToken)
                : await UserClient.VerifyOtpTokenAsync(_verifyRequest, cancellationToken: cancellationToken);

            if (response is null)
            {
                _errorMessage = "تأیید رمز پویا انجام نشد. لطفاً دوباره تلاش کنید.";
                return false;
            }

            if (response.Success)
            {
                if (!string.IsNullOrWhiteSpace(response.Token))
                {
                    await LocalStorage.SetItemAsync(TokenStorageKey, response.Token);
                }
                else
                {
                    await LocalStorage.RemoveItemAsync(TokenStorageKey);
                }

                await LocalStorage.RemoveItemAsync(PhoneStorageKey);
                await LocalStorage.RemoveItemAsync(RedirectStorageKey);
                await LocalStorage.RemoveItemAsync("referral:code");

                _attemptsLeft = MaxVerificationAttempts;
                _verifyRequest.Code = string.Empty;

               
           
                if (!InlineMode)
                {
                    MudDialog?.Close();
                }
                
                await OnLoginSuccess.InvokeAsync();
                await InvokeAsync(StateHasChanged);
                MainService.OnOnChangeHandler();
                // await OnLoginSuccessAsync();
                return true;
            }

            // اگه سرور RemainingAttempts برگردونده، از اون استفاده کن
            if (response.RemainingAttempts > 0)
                _attemptsLeft = response.RemainingAttempts;

            RegisterFailedAttempt(string.IsNullOrWhiteSpace(response.Message) ? "کد نادرست است." : response.Message);
        }
        catch (RpcException rpcEx)
        {
            await HandleVerificationFailureAsync(rpcEx);
        }
        catch (OperationCanceledException)
        {
            // ignored - user canceled operation
        }
        catch (Exception ex)
        {
            _errorMessage = ex.Message;
        }
        finally
        {
            _isBusy = false;
            ClearOperationToken();
            await InvokeAsync(StateHasChanged);
        }

        return false;
    }

   
    private async Task HandleVerificationFailureAsync(RpcException rpcEx)
    {
        switch (rpcEx.Status.StatusCode)
        {
            case StatusCode.PermissionDenied:
            case StatusCode.InvalidArgument:
                RegisterFailedAttempt(!string.IsNullOrWhiteSpace(rpcEx.Status.Detail)
                    ? rpcEx.Status.Detail
                    : "کد نادرست است.");
                break;
            case StatusCode.Unauthenticated:
                await ResetAuthenticationAsync();
                _errorMessage = "نشست کاربری منقضی شده است. لطفاً دوباره وارد شوید.";
                _currentStep = AuthStep.Phone;
                break;
            case StatusCode.DeadlineExceeded:
            case StatusCode.NotFound:
                _errorMessage = "کد منقضی شده است. لطفاً رمز جدید دریافت کنید.";
                break;
            default:
                _errorMessage = !string.IsNullOrWhiteSpace(rpcEx.Status.Detail)
                    ? rpcEx.Status.Detail
                    : "تأیید رمز پویا انجام نشد. لطفاً دوباره تلاش کنید.";
                break;
        }
    }

    private void RegisterFailedAttempt(string baseMessage)
    {
        _attemptsLeft = Math.Max(0, _attemptsLeft - 1);
        _errorMessage = _attemptsLeft > 0
            ? $"{baseMessage} {_attemptsLeft} تلاش باقی مانده است."
            : $"{baseMessage} تلاش‌های مجاز شما به پایان رسیده است. لطفاً رمز جدید دریافت کنید.";
    }

    private async Task ResendOtpAsync()
    {
        if (_resendRemaining > 0 || _isBusy || string.IsNullOrWhiteSpace(_phoneNumber))
            return;

        _errorMessage = null;
        _infoMessage = null;
        _isBusy = true;
        var cancellationToken = PrepareOperationToken();

        try
        {
            var request = new CreateNewOtpTokenRequest { Mobile = _phoneNumber, Purpose = OtpPurpose };
            var metadata = await BuildAuthMetadataAsync();
            CreateNewOtpTokenResponse response = metadata is not null
                ? await UserClient.CreateNewOtpTokenAsync(request, metadata, cancellationToken: cancellationToken)
                : await UserClient.CreateNewOtpTokenAsync(request, cancellationToken: cancellationToken);

            if (response?.Success != true)
            {
                _errorMessage = string.IsNullOrWhiteSpace(response?.Message)
                    ? "ارسال مجدد رمز پویا با خطا مواجه شد."
                    : response!.Message;
                return;
            }

            _infoMessage = string.IsNullOrWhiteSpace(response.Message) ? "کد جدید ارسال شد." : response.Message;
            _attemptsLeft = MaxVerificationAttempts;
            _verifyRequest.Code = string.Empty;
            StartResendCountdown();
        }
        catch (RpcException rpcEx)
        {
            await HandleResendFailureAsync(rpcEx);
        }
        catch (OperationCanceledException)
        {
            // ignored
        }
        catch (Exception ex)
        {
            _errorMessage = ex.Message;
        }
        finally
        {
            _isBusy = false;
            ClearOperationToken();
            await InvokeAsync(StateHasChanged);
        }
    }

    private async Task HandleResendFailureAsync(RpcException rpcEx)
    {
        switch (rpcEx.Status.StatusCode)
        {
            case StatusCode.Unauthenticated:
                await ResetAuthenticationAsync();
                _errorMessage = "نشست کاربری منقضی شده است. لطفاً دوباره وارد شوید.";
                _currentStep = AuthStep.Phone;
                break;
            default:
                _errorMessage = !string.IsNullOrWhiteSpace(rpcEx.Status.Detail)
                    ? rpcEx.Status.Detail
                    : "ارسال مجدد رمز پویا با خطا مواجه شد.";
                break;
        }
    }

    private async Task ChangePhoneAsync()
    {
        await LocalStorage.RemoveItemAsync(PhoneStorageKey);
        _currentStep = AuthStep.Phone;
        _phoneNumber = null;
        _verifyRequest.Code = string.Empty;
        _attemptsLeft = MaxVerificationAttempts;
        _resendTimer?.Dispose();
        _resendTimer = null;
        _resendRemaining = 0;
        // if (EnableCaptcha)
            GenerateCaptcha();
    }

    private void StartResendCountdown(int seconds = DefaultResendCooldown)
    {
        _resendRemaining = seconds;
        _resendTimer?.Dispose();
        _resendTimer = new Timer(_ =>
        {
            var remaining = Interlocked.Add(ref _resendRemaining, -1);
            if (remaining <= 0)
            {
                Interlocked.Exchange(ref _resendRemaining, 0);
                _resendTimer?.Dispose();
                _resendTimer = null;
            }
            _ = InvokeAsync(StateHasChanged);
        }, null, 1000, 1000);
    }

    private async Task<MetadataAlias?> BuildAuthMetadataAsync()
    {
        var token = await LocalStorage.GetItemAsync<string>(TokenStorageKey);
        if (string.IsNullOrWhiteSpace(token)) return null;
        return new MetadataAlias { { "Authorization", $"Bearer {token}" } };
    }

    private async Task ResetAuthenticationAsync()
    {
        await LocalStorage.RemoveItemAsync(TokenStorageKey);
        await LocalStorage.RemoveItemAsync(PhoneStorageKey);
        await LocalStorage.RemoveItemAsync(RedirectStorageKey);
    }

    private CancellationToken PrepareOperationToken()
    {
        _operationCts?.Cancel();
        _operationCts?.Dispose();
        _operationCts = new CancellationTokenSource();
        return _operationCts.Token;
    }

    private void ClearOperationToken()
    {
        _operationCts?.Dispose();
        _operationCts = null;
    }

    private void Cancel()
    {
        if (!InlineMode)
            MudDialog?.Close();
    }

    public void Dispose()
    {
        _operationCts?.Cancel();
        _operationCts?.Dispose();
        _operationCts = null;
        _resendTimer?.Dispose();
        _resendTimer = null;
    }

    private string GetDialogTitle() => _currentStep == AuthStep.Phone ? "ورود/ثبت‌نام به حساب کاربری" : "تأیید رمز پویا";
}
