import 'dart:convert';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart' as http;
import 'package:http_interceptor/http_interceptor.dart';
import 'package:jwt_decoder/jwt_decoder.dart';
import '../models/user_profile.dart';
import '../utils/logging_interceptor.dart';

class AuthService {
  static const String _tokenKey = 'auth_token';
  static const String _refreshTokenKey = 'refresh_token';
  static const String _tokenEndpoint = 'https://auth.goutezplanb.com/realms/planb-internal/protocol/openid-connect/token';
  static const String _clientId = 'delivery-mobile-app';

  final FlutterSecureStorage _secureStorage;
  final http.Client _httpClient;

  AuthService({
    FlutterSecureStorage? secureStorage,
    http.Client? httpClient,
  })  : _secureStorage = secureStorage ?? const FlutterSecureStorage(
          aOptions: AndroidOptions(
            encryptedSharedPreferences: true,
          ),
          iOptions: IOSOptions(
            accessibility: KeychainAccessibility.first_unlock,
          ),
          mOptions: MacOsOptions(
            accessibility: KeychainAccessibility.first_unlock,
          ),
        ),
        _httpClient = httpClient ?? InterceptedClient.build(
          interceptors: [LoggingInterceptor()],
        );

  Future<AuthResult> login({
    required String username,
    required String password,
  }) async {
    try {
      final response = await _httpClient.post(
        Uri.parse(_tokenEndpoint),
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: {
          'grant_type': 'password',
          'client_id': _clientId,
          'username': username,
          'password': password,
          'scope': 'openid profile offline_access',
        },
      );

      if (response.statusCode == 200) {
        final data = json.decode(response.body) as Map<String, dynamic>;
        final accessToken = data['access_token'] as String;
        final refreshToken = data['refresh_token'] as String?;

        await _secureStorage.write(key: _tokenKey, value: accessToken);
        if (refreshToken != null) {
          await _secureStorage.write(key: _refreshTokenKey, value: refreshToken);
        }

        return AuthResult.success(token: accessToken);
      } else if (response.statusCode == 401) {
        return AuthResult.error(error: 'Invalid username or password');
      } else {
        return AuthResult.error(error: 'Authentication failed: ${response.statusCode}');
      }
    } catch (e) {
      return AuthResult.error(error: 'Network error: ${e.toString()}');
    }
  }

  Future<AuthResult> refreshAccessToken() async {
    try {
      final refreshToken = await getRefreshToken();
      if (refreshToken == null) {
        return AuthResult.error(error: 'No refresh token available');
      }

      final response = await _httpClient.post(
        Uri.parse(_tokenEndpoint),
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: {
          'grant_type': 'refresh_token',
          'client_id': _clientId,
          'refresh_token': refreshToken,
        },
      );

      if (response.statusCode == 200) {
        final data = json.decode(response.body) as Map<String, dynamic>;
        final accessToken = data['access_token'] as String;
        final newRefreshToken = data['refresh_token'] as String?;

        await _secureStorage.write(key: _tokenKey, value: accessToken);
        if (newRefreshToken != null) {
          await _secureStorage.write(key: _refreshTokenKey, value: newRefreshToken);
        }

        return AuthResult.success(token: accessToken);
      } else {
        await logout();
        return AuthResult.error(error: 'Token refresh failed');
      }
    } catch (e) {
      return AuthResult.error(error: 'Token refresh error: ${e.toString()}');
    }
  }

  Future<void> logout() async {
    await Future.wait([
      _secureStorage.delete(key: _tokenKey),
      _secureStorage.delete(key: _refreshTokenKey),
    ]);
  }

  Future<String?> getToken() async {
    return await _secureStorage.read(key: _tokenKey);
  }

  Future<String?> getRefreshToken() async {
    return await _secureStorage.read(key: _refreshTokenKey);
  }

  bool isTokenValid(String? token) {
    if (token == null || token.isEmpty) return false;
    try {
      return !JwtDecoder.isExpired(token);
    } catch (e) {
      return false;
    }
  }

  UserProfile? decodeToken(String token) {
    try {
      final decodedToken = JwtDecoder.decode(token);
      return UserProfile.fromJwtClaims(decodedToken);
    } catch (e) {
      return null;
    }
  }

  Future<bool> isAuthenticated() async {
    final token = await getToken();
    return isTokenValid(token);
  }
}

sealed class AuthResult {
  const AuthResult();

  factory AuthResult.success({required String token}) => _Success(token);
  factory AuthResult.error({required String error}) => _Error(error);
  const factory AuthResult.cancelled() = _Cancelled;

  R when<R>({
    required R Function(String token) success,
    required R Function(String error) onError,
    required R Function() cancelled,
  }) {
    return switch (this) {
      _Success(:final token) => success(token),
      _Error(:final error) => onError(error),
      _Cancelled() => cancelled(),
    };
  }
}

final class _Success extends AuthResult {
  final String token;
  const _Success(this.token);
}

final class _Error extends AuthResult {
  final String error;
  const _Error(this.error);
}

final class _Cancelled extends AuthResult {
  const _Cancelled();
}
