import 'package:hl_lieferservice/feature/authentication/model/user.dart';

abstract class AuthState {}

/// Initialer State beim App-Start: der Refresh-Token aus der Secure
/// Storage wird gerade gegen das Issuer-Endpoint geprüft. Die UI zeigt
/// in dieser Zeit einen Splash statt LoginPage, damit Nutzer mit
/// gespeicherter Session keinen sichtbaren Login-Flash sehen.
class AuthBootstrapping extends AuthState {}

class Unauthenticated extends AuthState {
  final bool sessionExpired;

  /// `true`, wenn der letzte Login-Versuch in das 10-s-Timeout im
  /// [AuthBloc] gelaufen ist (z. B. Verbindungsabbruch während
  /// `tokenProvider.login()` oder hängender Issuer). Die [LoginPage]
  /// blendet daraufhin einen Hinweis ein.
  final bool loginTimedOut;

  Unauthenticated({this.sessionExpired = false, this.loginTimedOut = false});
}

/// Transient state während dem PKCE-Flow (Browser-Tab offen,
/// Token-Tausch läuft).
class Authenticating extends AuthState {}

/// Erfolgreiche Authentifizierung. Trägt sowohl die "neuen"
/// Token-/Claim-Felder als auch das Legacy-User-Objekt — Letzteres
/// wird aus den Claims befüllt und mit Phase D komplett entfernt.
class Authenticated extends AuthState {
  Authenticated({
    required this.personalnummer,
    required this.displayName,
    required this.email,
    required this.user,
    required this.sessionId,
  });

  /// Personalnummer aus dem `personalnummer`-Custom-Claim. Das
  /// Backend nutzt diese als Account-Identifier.
  final int personalnummer;

  /// Beste verfügbare Beschriftung für den Fahrer:
  /// `name` > `given_name + family_name` > `preferred_username`.
  final String displayName;

  /// E-Mail aus dem `email`-Claim, falls vorhanden.
  final String? email;

  /// Legacy: das alte `User`-Domänenobjekt, aus den Claims befüllt.
  /// Wird in Phase D entfernt, sobald keine Aufrufer mehr da sind.
  final User user;

  /// Legacy: füllt für die alten `getSessionOrThrow()`-Aufrufer
  /// einen Wert — die laufen sowieso gegen ERPframe und werden in
  /// Phase D abgelöst.
  final String sessionId;

  /// Konstruiert den State aus dem dekodierten ID-Token-Payload.
  factory Authenticated.fromClaims({
    required Map<String, dynamic> claims,
    required String accessToken,
  }) {
    final personalnummerRaw = claims['personalnummer'];
    final personalnummer = switch (personalnummerRaw) {
      int v => v,
      String v => int.parse(v),
      _ => throw FormatException(
        'personalnummer-Claim fehlt oder hat unerwarteten Typ '
        '(${personalnummerRaw.runtimeType})',
      ),
    };

    final given = (claims['given_name'] as String?) ?? '';
    final family = (claims['family_name'] as String?) ?? '';
    final preferredUsername =
        (claims['preferred_username'] as String?) ?? 'fahrer';
    final fullName = (claims['name'] as String?)?.trim();

    final displayName =
        fullName != null && fullName.isNotEmpty
            ? fullName
            : (given.isNotEmpty || family.isNotEmpty
                ? '$given $family'.trim()
                : preferredUsername);

    final email = claims['email'] as String?;

    final user = User(
      number: personalnummer.toString(),
      firstName: given,
      lastName: family,
      mail: email ?? '',
    );

    return Authenticated(
      personalnummer: personalnummer,
      displayName: displayName,
      email: email,
      user: user,
      sessionId: accessToken,
    );
  }
}
