import 'dart:convert';
import 'dart:io';

import 'package:http/http.dart' as http;

import '../../../../core/config/app_config.dart';
import '../../../../core/errors/exceptions.dart';
import '../../../../core/logging/app_logger.dart';
import '../models/token_model.dart';
import '../models/user_model.dart';

abstract class AuthRemoteDataSource {
  Future<UserModel> register({
    required String email,
    required String password,
    required String name,
  });

  Future<TokenModel> login({
    required String email,
    required String password,
  });

  Future<UserModel> getCurrentUser(String accessToken);

  Future<TokenModel> refreshToken(String refreshToken);
}

class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
  final AppLogger logger;
  final http.Client _client;

  String get _baseUrl => AppConfig.current.apiBaseUrl;

  AuthRemoteDataSourceImpl({
    required this.logger,
    http.Client? client,
  }) : _client = client ?? http.Client();

  Map<String, String> get _headers => {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      };

  Map<String, String> _authHeaders(String token) => {
        ..._headers,
        'Authorization': 'Bearer $token',
      };

  @override
  Future<UserModel> register({
    required String email,
    required String password,
    required String name,
  }) async {
    final uri = Uri.parse('$_baseUrl/auth/register');
    logger.info('POST $uri');

    try {
      final response = await _client.post(
        uri,
        headers: _headers,
        body: json.encode({
          'email': email,
          'password': password,
          'name': name,
        }),
      );
      logger.debug('Response: ${response.statusCode}');

      if (response.statusCode == 201 || response.statusCode == 200) {
        return UserModel.fromJson(json.decode(response.body));
      } else if (response.statusCode == 400) {
        final body = json.decode(response.body);
        throw AuthException(
          message: body['detail'] ?? 'Registration failed',
        );
      } else {
        throw ServerException(
          message: 'Registration failed',
          statusCode: response.statusCode,
        );
      }
    } on SocketException {
      throw const NetworkException();
    }
  }

  @override
  Future<TokenModel> login({
    required String email,
    required String password,
  }) async {
    final uri = Uri.parse('$_baseUrl/auth/login');
    logger.info('POST $uri');

    try {
      final response = await _client.post(
        uri,
        headers: _headers,
        body: json.encode({
          'email': email,
          'password': password,
        }),
      );
      logger.debug('Response: ${response.statusCode}');

      if (response.statusCode == 200) {
        return TokenModel.fromJson(json.decode(response.body));
      } else if (response.statusCode == 401) {
        throw const AuthException(message: 'Invalid email or password');
      } else {
        throw ServerException(
          message: 'Login failed',
          statusCode: response.statusCode,
        );
      }
    } on SocketException {
      throw const NetworkException();
    }
  }

  @override
  Future<UserModel> getCurrentUser(String accessToken) async {
    final uri = Uri.parse('$_baseUrl/auth/me');
    logger.info('GET $uri');

    try {
      final response = await _client.get(
        uri,
        headers: _authHeaders(accessToken),
      );
      logger.debug('Response: ${response.statusCode}');

      if (response.statusCode == 200) {
        return UserModel.fromJson(json.decode(response.body));
      } else if (response.statusCode == 401) {
        throw const UnauthorizedException();
      } else {
        throw ServerException(
          message: 'Failed to get user info',
          statusCode: response.statusCode,
        );
      }
    } on SocketException {
      throw const NetworkException();
    }
  }

  @override
  Future<TokenModel> refreshToken(String refreshToken) async {
    final uri = Uri.parse('$_baseUrl/auth/refresh');
    logger.info('POST $uri');

    try {
      final response = await _client.post(
        uri,
        headers: _headers,
        body: json.encode({'refresh_token': refreshToken}),
      );
      logger.debug('Response: ${response.statusCode}');

      if (response.statusCode == 200) {
        return TokenModel.fromJson(json.decode(response.body));
      } else if (response.statusCode == 401) {
        throw const UnauthorizedException(message: 'Session expired');
      } else {
        throw ServerException(
          message: 'Token refresh failed',
          statusCode: response.statusCode,
        );
      }
    } on SocketException {
      throw const NetworkException();
    }
  }
}
