import 'dart:async';

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../../../core/constants/error_whitelist.dart';
import '../../../core/i18n/locale_utils.dart';
import '../../../core/services/auth_proxy_service.dart';
import '../../../core/services/logout_service.dart';
import '../../../core/widgets/theme_toggle_button.dart';
import 'package:userfront/i18n.dart';

class ErrorScreen extends StatefulWidget {
  final String? errorId;
  final String? errorCode;
  final String? description;
  final bool? isProdOverride;
  final Future<Map<String, dynamic>> Function()? sessionProfileLoader;
  final Map<String, dynamic>? tenantAccessDetails;

  const ErrorScreen({
    super.key,
    this.errorId,
    this.errorCode,
    this.description,
    this.isProdOverride,
    this.sessionProfileLoader,
    this.tenantAccessDetails,
  });

  @override
  State<ErrorScreen> createState() => _ErrorScreenState();
}

class _ErrorScreenState extends State<ErrorScreen> {
  Map<String, dynamic>? _sessionProfile;
  bool _isLoadingSessionProfile = false;
  String? _sessionProfileError;

  bool get _isTenantAccessBlocked =>
      (widget.errorCode ?? '').trim() == 'tenant_not_allowed';

  @override
  void initState() {
    super.initState();
    if (_isTenantAccessBlocked && _shouldLoadSessionProfile()) {
      unawaited(_loadSessionProfile());
    }
  }

  Map<String, dynamic>? get _tenantAccessDetails => widget.tenantAccessDetails;

  bool _shouldLoadSessionProfile() {
    final details = _tenantAccessDetails;
    if (details == null) {
      return true;
    }
    final hasAccount = _extractAccountEmail(details).isNotEmpty;
    final hasTenant = _extractCurrentTenantLabel(details).isNotEmpty;
    return !hasAccount || !hasTenant;
  }

  Future<void> _loadSessionProfile() async {
    if (_isLoadingSessionProfile) {
      return;
    }
    setState(() {
      _isLoadingSessionProfile = true;
      _sessionProfileError = null;
    });
    try {
      final loader = widget.sessionProfileLoader ?? AuthProxyService.getMe;
      final profile = await loader();
      if (!mounted) {
        return;
      }
      setState(() {
        _sessionProfile = profile;
      });
    } catch (error) {
      if (!mounted) {
        return;
      }
      setState(() {
        _sessionProfileError = error.toString();
      });
    } finally {
      if (mounted) {
        setState(() {
          _isLoadingSessionProfile = false;
        });
      }
    }
  }

  String _extractTenantLabel(Map<String, dynamic>? profile) {
    if (profile == null) {
      return '';
    }

    final tenant = profile['tenant'];
    if (tenant is Map) {
      final name = tenant['name']?.toString().trim() ?? '';
      if (name.isNotEmpty) {
        return name;
      }
      final slug = tenant['slug']?.toString().trim() ?? '';
      if (slug.isNotEmpty) {
        return slug;
      }
    }

    final joinedTenants = profile['joinedTenants'];
    if (joinedTenants is List) {
      for (final item in joinedTenants) {
        if (item is Map) {
          final name = item['name']?.toString().trim() ?? '';
          if (name.isNotEmpty) {
            return name;
          }
          final slug = item['slug']?.toString().trim() ?? '';
          if (slug.isNotEmpty) {
            return slug;
          }
        }
      }
    }

    return '';
  }

  String _extractCurrentTenantLabel(Map<String, dynamic>? details) {
    if (details == null) {
      return '';
    }
    final tenant = details['current_tenant'];
    if (tenant is! Map) {
      return '';
    }

    final name = tenant['name']?.toString().trim() ?? '';
    if (name.isNotEmpty) {
      return name;
    }
    final slug = tenant['slug']?.toString().trim() ?? '';
    if (slug.isNotEmpty) {
      return slug;
    }
    final identifier = tenant['identifier']?.toString().trim() ?? '';
    if (identifier.isNotEmpty) {
      return identifier;
    }
    final id = tenant['id']?.toString().trim() ?? '';
    return id;
  }

  String _extractAccountEmail(Map<String, dynamic>? details) {
    if (details == null) {
      return '';
    }
    final account = details['account'];
    if (account is! Map) {
      return '';
    }
    return account['email']?.toString().trim() ?? '';
  }

  List<String> _extractAllowedTenantLabels(Map<String, dynamic>? details) {
    if (details == null) {
      return const [];
    }
    final raw = details['allowed_tenants'];
    if (raw is! List) {
      return const [];
    }

    final labels = <String>[];
    for (final item in raw) {
      if (item is! Map) {
        continue;
      }
      final label =
          item['name']?.toString().trim() ??
          item['slug']?.toString().trim() ??
          item['identifier']?.toString().trim() ??
          item['id']?.toString().trim() ??
          '';
      if (label.isNotEmpty) {
        labels.add(label);
      }
    }
    return labels;
  }

  List<String> _extractAffiliatedTenantLabelsFromDetails(
    Map<String, dynamic>? details,
  ) {
    if (details == null) {
      return const [];
    }
    final raw = details['affiliated_tenants'];
    if (raw is! List) {
      return const [];
    }

    final labels = <String>[];
    for (final item in raw) {
      if (item is! Map) {
        continue;
      }
      final label =
          item['name']?.toString().trim() ??
          item['slug']?.toString().trim() ??
          item['identifier']?.toString().trim() ??
          item['id']?.toString().trim() ??
          '';
      if (label.isNotEmpty) {
        labels.add(label);
      }
    }
    return labels;
  }

  List<String> _extractAffiliatedTenantLabelsFromProfile(
    Map<String, dynamic>? profile,
  ) {
    if (profile == null) {
      return const [];
    }

    final labels = <String>[];
    final seen = <String>{};

    void appendLabel(String value) {
      final trimmed = value.trim();
      if (trimmed.isEmpty || seen.contains(trimmed)) {
        return;
      }
      seen.add(trimmed);
      labels.add(trimmed);
    }

    final joinedTenants = profile['joinedTenants'];
    if (joinedTenants is List) {
      for (final item in joinedTenants) {
        if (item is Map) {
          appendLabel(item['name']?.toString() ?? '');
          appendLabel(item['slug']?.toString() ?? '');
        }
      }
    }

    final tenant = _extractTenantLabel(profile);
    if (tenant.isNotEmpty) {
      appendLabel(tenant);
    }

    return labels;
  }

  Future<void> _switchAccount() async {
    await LogoutService().logout();
    if (!mounted) {
      return;
    }
    context.go(buildLocalizedSigninPath(Uri.base));
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;
    final isProd = widget.isProdOverride ?? AuthProxyService.isProdEnv;
    final normalizedCode = (widget.errorCode ?? '').trim();
    final hasCode = normalizedCode.isNotEmpty;
    final internalWhitelistKey =
        internalErrorWhitelistMessageKeys[normalizedCode];
    final isInternalWhitelisted = internalWhitelistKey != null;
    final isOryBypass = hasCode && oryBypassErrorCodes.contains(normalizedCode);
    final isKnownProdCode = hasCode && (isInternalWhitelisted || isOryBypass);
    final isTenantAccessBlocked = normalizedCode == 'tenant_not_allowed';
    final errorType = isProd
        ? (isKnownProdCode ? normalizedCode : 'unknown_error')
        : (hasCode ? normalizedCode : 'unknown_error');
    final title = isTenantAccessBlocked
        ? tr(
            'msg.userfront.error.tenant.page_title',
            fallback: 'Application access is restricted',
          )
        : isProd
        ? tr('msg.userfront.error.title')
        : (hasCode
              ? tr(
                  'msg.userfront.error.title_with_code',
                  params: {'code': normalizedCode},
                )
              : tr('msg.userfront.error.title_generic'));
    final tenantLabelFromDetails = _extractCurrentTenantLabel(
      _tenantAccessDetails,
    );
    final tenantLabel = tenantLabelFromDetails.isNotEmpty
        ? tenantLabelFromDetails
        : _extractTenantLabel(_sessionProfile);
    final emailFromDetails = _extractAccountEmail(_tenantAccessDetails);
    final emailLabel = emailFromDetails.isNotEmpty
        ? emailFromDetails
        : (_sessionProfile?['email']?.toString().trim() ?? '');
    final affiliatedTenantLabels =
        _extractAffiliatedTenantLabelsFromDetails(
          _tenantAccessDetails,
        ).isNotEmpty
        ? _extractAffiliatedTenantLabelsFromDetails(_tenantAccessDetails)
        : _extractAffiliatedTenantLabelsFromProfile(_sessionProfile);
    final allowedTenantLabels = _extractAllowedTenantLabels(
      _tenantAccessDetails,
    );
    final isLoadingTenantContext =
        _isLoadingSessionProfile && _tenantAccessDetails == null;
    final hasTenantLookupFailure =
        _sessionProfileError != null &&
        _sessionProfileError!.isNotEmpty &&
        _tenantAccessDetails == null;
    final showTenantLookupFallback =
        _tenantAccessDetails == null &&
        (emailLabel.isEmpty || tenantLabel.isEmpty);
    final internalWhitelistDetail = internalWhitelistKey == null
        ? null
        : tr(internalWhitelistKey);
    final detail = isTenantAccessBlocked
        ? tr(
            'msg.userfront.error.tenant.detail',
            fallback:
                'The current signed-in account cannot access this application.',
          )
        : isProd
        ? (isInternalWhitelisted
              ? internalWhitelistDetail!
              : (isOryBypass
                    ? tr(
                        'msg.userfront.error.ory.$normalizedCode',
                        fallback: (widget.description?.isNotEmpty == true)
                            ? widget.description
                            : tr('msg.userfront.error.detail_request'),
                      )
                    : tr('msg.userfront.error.detail_contact')))
        : ((widget.description?.isNotEmpty == true)
              ? widget.description!
              : (hasCode
                    ? tr('msg.userfront.error.detail_generic')
                    : tr('msg.userfront.error.detail_request')));
    return Scaffold(
      backgroundColor: colorScheme.surfaceContainerLowest,
      body: SafeArea(
        child: LayoutBuilder(
          builder: (context, constraints) => SingleChildScrollView(
            padding: const EdgeInsets.all(24),
            child: ConstrainedBox(
              constraints: BoxConstraints(
                minHeight: constraints.maxHeight - 48,
              ),
              child: Center(
                child: ConstrainedBox(
                  constraints: const BoxConstraints(maxWidth: 560),
                  child: Card(
                    margin: EdgeInsets.zero,
                    elevation: 0,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(16),
                      side: BorderSide(color: colorScheme.outlineVariant),
                    ),
                    child: Padding(
                      padding: const EdgeInsets.fromLTRB(28, 28, 28, 24),
                      child: Column(
                        mainAxisSize: MainAxisSize.min,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Row(
                            children: [
                              Expanded(
                                child: Text(
                                  title,
                                  style: theme.textTheme.titleLarge?.copyWith(
                                    fontWeight: FontWeight.w700,
                                    color: colorScheme.onSurface,
                                  ),
                                ),
                              ),
                              const ThemeToggleButton(compact: true),
                            ],
                          ),
                          const SizedBox(height: 12),
                          Text(
                            detail,
                            style: theme.textTheme.bodyMedium?.copyWith(
                              color: colorScheme.onSurfaceVariant,
                              height: 1.5,
                            ),
                          ),
                          if (isTenantAccessBlocked) ...[
                            const SizedBox(height: 16),
                            Container(
                              width: double.infinity,
                              padding: const EdgeInsets.all(16),
                              decoration: BoxDecoration(
                                color: colorScheme.surface,
                                borderRadius: BorderRadius.circular(12),
                                border: Border.all(
                                  color: colorScheme.outlineVariant,
                                ),
                              ),
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Text(
                                    tr(
                                      'msg.userfront.error.tenant.title',
                                      fallback: 'Access restriction details',
                                    ),
                                    style: theme.textTheme.titleSmall?.copyWith(
                                      fontWeight: FontWeight.w700,
                                    ),
                                  ),
                                  const SizedBox(height: 12),
                                  if (isLoadingTenantContext)
                                    Row(
                                      crossAxisAlignment:
                                          CrossAxisAlignment.start,
                                      children: [
                                        SizedBox(
                                          width: 16,
                                          height: 16,
                                          child: CircularProgressIndicator(
                                            strokeWidth: 2,
                                            color: colorScheme.primary,
                                          ),
                                        ),
                                        const SizedBox(width: 10),
                                        Flexible(
                                          child: Text(
                                            tr(
                                              'msg.userfront.error.tenant.loading',
                                              fallback:
                                                  'Loading the current account details.',
                                            ),
                                            style: theme.textTheme.bodySmall
                                                ?.copyWith(
                                                  color: colorScheme
                                                      .onSurfaceVariant,
                                                ),
                                          ),
                                        ),
                                      ],
                                    )
                                  else ...[
                                    _InfoRow(
                                      label: tr(
                                        'msg.userfront.error.tenant.account',
                                        fallback: 'Account',
                                      ),
                                      value: emailLabel.isNotEmpty
                                          ? emailLabel
                                          : tr(
                                              'msg.userfront.error.tenant.account_unknown',
                                              fallback: 'Unknown',
                                            ),
                                    ),
                                    const SizedBox(height: 8),
                                    _InfoRow(
                                      label: tr(
                                        'msg.userfront.error.tenant.primary_tenant',
                                        fallback: 'Primary affiliated tenant',
                                      ),
                                      value: tenantLabel.isNotEmpty
                                          ? tenantLabel
                                          : tr(
                                              'msg.userfront.error.tenant.tenant_unknown',
                                              fallback: 'Unknown',
                                            ),
                                    ),
                                    const SizedBox(height: 8),
                                    _InfoRow(
                                      label: tr(
                                        'msg.userfront.error.tenant.affiliated_tenants',
                                        fallback: 'All affiliated tenants',
                                      ),
                                      value: affiliatedTenantLabels.isNotEmpty
                                          ? affiliatedTenantLabels.join(', ')
                                          : tr(
                                              'msg.userfront.error.tenant.tenant_unknown',
                                              fallback: 'Unknown',
                                            ),
                                    ),
                                    if (showTenantLookupFallback) ...[
                                      const SizedBox(height: 12),
                                      Text(
                                        tr(
                                          'msg.userfront.error.tenant.lookup_fallback',
                                          fallback:
                                              'Some fields may be unavailable because there is not enough profile information to display.',
                                        ),
                                        style: theme.textTheme.bodySmall
                                            ?.copyWith(
                                              color:
                                                  colorScheme.onSurfaceVariant,
                                            ),
                                      ),
                                    ],
                                    if (hasTenantLookupFailure) ...[
                                      const SizedBox(height: 12),
                                      Text(
                                        tr(
                                          'msg.userfront.error.tenant.load_failed',
                                          fallback:
                                              'Failed to load account details. Please try again.',
                                        ),
                                        style: theme.textTheme.bodySmall
                                            ?.copyWith(
                                              color:
                                                  colorScheme.onSurfaceVariant,
                                            ),
                                      ),
                                    ],
                                  ],
                                ],
                              ),
                            ),
                            const SizedBox(height: 12),
                            Container(
                              width: double.infinity,
                              padding: const EdgeInsets.all(16),
                              decoration: BoxDecoration(
                                color: colorScheme.surface,
                                borderRadius: BorderRadius.circular(12),
                                border: Border.all(
                                  color: colorScheme.outlineVariant,
                                ),
                              ),
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Text(
                                    tr(
                                      'msg.userfront.error.tenant.allowed_box_title',
                                      fallback: 'Allowed tenants',
                                    ),
                                    style: theme.textTheme.titleSmall?.copyWith(
                                      fontWeight: FontWeight.w700,
                                    ),
                                  ),
                                  const SizedBox(height: 12),
                                  if (allowedTenantLabels.isNotEmpty) ...[
                                    _InfoRow(
                                      label: tr(
                                        'msg.userfront.error.tenant.allowed_tenants',
                                        fallback: 'Allowed tenants',
                                      ),
                                      value: allowedTenantLabels.join(', '),
                                    ),
                                  ] else ...[
                                    _InfoRow(
                                      label: tr(
                                        'msg.userfront.error.tenant.allowed_tenants',
                                        fallback: 'Allowed tenants',
                                      ),
                                      value: tr(
                                        'msg.userfront.error.tenant.tenant_unknown',
                                        fallback: 'Unknown',
                                      ),
                                    ),
                                  ],
                                ],
                              ),
                            ),
                          ],
                          const SizedBox(height: 12),
                          Text(
                            tr(
                              'msg.userfront.error.type',
                              params: {'type': errorType},
                            ),
                            style: theme.textTheme.bodySmall?.copyWith(
                              color: colorScheme.onSurfaceVariant,
                            ),
                          ),
                          if (widget.errorId != null &&
                              widget.errorId!.isNotEmpty) ...[
                            const SizedBox(height: 12),
                            Text(
                              tr(
                                'msg.userfront.error.id',
                                params: {'id': widget.errorId!},
                              ),
                              style: theme.textTheme.bodySmall?.copyWith(
                                color: colorScheme.onSurfaceVariant,
                              ),
                            ),
                          ],
                          const SizedBox(height: 20),
                          Wrap(
                            spacing: 12,
                            runSpacing: 12,
                            children: [
                              ElevatedButton(
                                onPressed: isTenantAccessBlocked
                                    ? _switchAccount
                                    : () => context.go('/login'),
                                style: ElevatedButton.styleFrom(
                                  backgroundColor: colorScheme.primary,
                                  foregroundColor: colorScheme.onPrimary,
                                  padding: const EdgeInsets.symmetric(
                                    horizontal: 16,
                                    vertical: 12,
                                  ),
                                  shape: RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(10),
                                  ),
                                ),
                                child: Text(
                                  isTenantAccessBlocked
                                      ? tr('ui.userfront.error.switch_account')
                                      : tr('ui.userfront.error.go_login'),
                                ),
                              ),
                              OutlinedButton(
                                onPressed: () => context.go(
                                  buildLocalizedHomePath(Uri.base),
                                ),
                                style: OutlinedButton.styleFrom(
                                  foregroundColor: colorScheme.onSurface,
                                  padding: const EdgeInsets.symmetric(
                                    horizontal: 16,
                                    vertical: 12,
                                  ),
                                  side: BorderSide(color: colorScheme.outline),
                                  shape: RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(10),
                                  ),
                                ),
                                child: Text(tr('ui.userfront.error.go_home')),
                              ),
                            ],
                          ),
                        ],
                      ),
                    ),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class _InfoRow extends StatelessWidget {
  final String label;
  final String value;

  const _InfoRow({required this.label, required this.value});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;
    return Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        SizedBox(
          width: 100,
          child: Text(
            label,
            style: theme.textTheme.bodySmall?.copyWith(
              color: colorScheme.onSurfaceVariant,
              fontWeight: FontWeight.w600,
            ),
          ),
        ),
        Expanded(
          child: Text(
            value,
            style: theme.textTheme.bodySmall?.copyWith(
              color: colorScheme.onSurface,
            ),
          ),
        ),
      ],
    );
  }
}
