import 'dart:convert' show base64, utf8;
import 'dart:convert' show jsonDecode, jsonEncode;

import 'package:fotodocumentation/controller/base_controller.dart';
import 'package:fotodocumentation/dto/jwt_token_pair_dto.dart';
import 'package:fotodocumentation/main.dart' show logger;
import 'package:fotodocumentation/utils/di_container.dart';
import 'package:fotodocumentation/utils/jwt_token_storage.dart';
import 'package:http/http.dart' as http;

typedef AuthenticateReply = ({JwtTokenPairDto? jwtTokenPairDto});

abstract interface class LoginController {
  Future<AuthenticateReply> authenticate(String username, String password);
  Future<bool> refreshAccessToken();
  Future<bool> isUsingJwtAuth();
}

class LoginControllerImpl extends BaseController implements LoginController {
  final String path = "login";

  JwtTokenStorage get _jwtTokenStorage => DiContainer.get();

  @override
  Future<AuthenticateReply> authenticate(String username, String password) async {
    http.Client client = httpClientUtils.client;
    try {
      Header cred = _getLoginHeader(username, password);
      String uriStr = '${uriUtils.getBaseUrl()}$path';
      Uri uri = Uri.parse(uriStr);

      var response = await client.get(uri, headers: {cred.name: cred.value});
      if (response.statusCode == 200) {
        final Map<String, dynamic> data = Map.castFrom(jsonDecode(response.body));

        final tokenPair = JwtTokenPairDto.fromJson(data);

        // Store tokens securely
        await _jwtTokenStorage.saveTokens(tokenPair.accessToken, tokenPair.refreshToken);

        // Load user data using the new token
        return (jwtTokenPairDto: tokenPair);
      } else {
        logger.e('Authentication failed: ${response.statusCode} ${response.body}');
        return (jwtTokenPairDto: null);
      }
    } catch (e) {
      logger.e("Authentication error: $e");
      return (jwtTokenPairDto: null);
    }
  }

  @override
  Future<bool> refreshAccessToken() async {
    try {
      final refreshToken = await _jwtTokenStorage.getRefreshToken();
      if (refreshToken == null) {
        logger.i('No refresh token available');
        return false;
      }
      String uriStr = '${uriUtils.getBaseUrl()}$path/login/refresh';
      Uri uri = Uri.parse(uriStr);

      final response = await http.post(
        uri,
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode({
          'refreshToken': refreshToken,
        }),
      );

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        final newAccessToken = data['accessToken'] as String;

        // Update only the access token (keep same refresh token)
        await _jwtTokenStorage.updateAccessToken(newAccessToken);

        logger.d('Access token refreshed successfully');
        return true;
      } else {
        logger.d('Token refresh failed: ${response.statusCode} ${response.body}');
        return false;
      }
    } catch (e) {
      logger.e('Token refresh error: $e');
      return false;
    }
  }

  @override
  Future<bool> isUsingJwtAuth() async {
    return await _jwtTokenStorage.hasTokens();
  }

  Header _getLoginHeader(String username, String password) {
    String combined = "$username:$password";
    final bytes = utf8.encode(combined);
    String asBase64 = base64.encode(bytes);
    return Header("Authorization", "Basic $asBase64");
  }
}
