import 'package:json_annotation/json_annotation.dart';
import 'package:latlong2/latlong.dart';
import 'package:niogu_ecommerce_v1/core/enums/campaign_type.dart';
import 'package:niogu_ecommerce_v1/core/utils/converter.dart';
import 'package:niogu_ecommerce_v1/features/home/domain/entities/home.dart';

part 'home_model.g.dart';

@JsonSerializable()
class HomeResponse {
  @JsonKey(name: 'current_outlet')
  final CurrentOutletResponse currentOutlet;

  @JsonKey(name: 'campaign_by_outlets')
  final List<CampaignByOutletResponse> campaignByOutlets;

  @JsonKey(name: 'all_categories')
  final List<CategoryItemResponse> allCategories;

  @JsonKey(name: 'other_outlets')
  final List<OtherOutletResponse> otherOutlets;

  @JsonKey(name: 'product_recommendations')
  final List<ProductItemResponse> productRecommendations;

  @JsonKey(name: 'all_products')
  final List<ProductItemResponse> allProducts;

  HomeResponse({
    required this.currentOutlet,
    required this.campaignByOutlets,
    required this.allCategories,
    required this.otherOutlets,
    required this.productRecommendations,
    required this.allProducts,
  });

  factory HomeResponse.fromJson(Map<String, dynamic> json) =>
      _$HomeResponseFromJson(json);

  Map<String, dynamic> toJson() => _$HomeResponseToJson(this);

  Home toHome() {
    return Home(
      currentOutlet: currentOutlet.toCurrentOutlet(),
      campaigns: campaignByOutlets
          .map((campaign) => campaign.toCampaignByOutlet())
          .toList(),
      categories: allCategories
          .map((category) => category.toCategoryItem())
          .toList(),
      otherOutlets: otherOutlets
          .map((outlet) => outlet.toOtherOutlet())
          .toList(),
      recommendations: productRecommendations
          .map((product) => product.toProductItem())
          .toList(),
      allProducts: allProducts
          .map((product) => product.toProductItem())
          .toList(),
    );
  }
}

@JsonSerializable()
class OperationalServiceResponse {
  @JsonKey(name: 'online_open_time')
  final String onlineOpenTime;

  @JsonKey(name: 'online_close_time')
  final String onlineCloseTime;

  @JsonKey(name: 'is_close_service')
  final bool isCloseService;

  const OperationalServiceResponse({
    required this.onlineOpenTime,
    required this.onlineCloseTime,
    required this.isCloseService,
  });

  factory OperationalServiceResponse.fromJson(Map<String, dynamic> json) =>
      _$OperationalServiceResponseFromJson(json);

  Map<String, dynamic> toJson() => _$OperationalServiceResponseToJson(this);

  OperationalService toOperationalService() {
    return OperationalService(
      onlineOpenTime: onlineOpenTime,
      onlineCloseTime: onlineCloseTime,
      isCloseService: isCloseService,
    );
  }
}

@JsonSerializable()
class CurrentOutletResponse {
  final String id;

  final String name;

  @JsonKey(fromJson: toDouble)
  final double? latitude;

  @JsonKey(fromJson: toDouble)
  final double? longitude;

  final String? location;

  @JsonKey(name: 'is_active')
  final bool isActive;

  CurrentOutletResponse({
    required this.id,
    required this.name,
    required this.location,
    required this.latitude,
    required this.longitude,
    required this.isActive,
  });

  factory CurrentOutletResponse.fromJson(Map<String, dynamic> json) =>
      _$CurrentOutletResponseFromJson(json);

  Map<String, dynamic> toJson() => _$CurrentOutletResponseToJson(this);

  CurrentOutlet toCurrentOutlet() {
    LatLng? coordinate;

    if (latitude != null && longitude != null) {
      coordinate = LatLng(latitude!, longitude!);
    }

    return CurrentOutlet(
      id: id,
      name: name,
      location: location,
      coordinate: coordinate,
      isActive: isActive,
    );
  }
}

@JsonSerializable()
class CampaignByOutletResponse {
  @JsonKey(name: 'campaign_type')
  final String? campaignType;

  @JsonKey(name: 'action_ref_id')
  final String? actionRefId;

  @JsonKey(name: 'server_banner_url')
  final String? serverBannerUrl;

  CampaignByOutletResponse({
    required this.campaignType,
    required this.actionRefId,
    this.serverBannerUrl,
  });

  factory CampaignByOutletResponse.fromJson(Map<String, dynamic> json) =>
      _$CampaignByOutletResponseFromJson(json);

  Map<String, dynamic> toJson() => _$CampaignByOutletResponseToJson(this);

  CampaignByOutlet toCampaignByOutlet() {
    final campaignType = this.campaignType != null
        ? CampaignType.values.byName(this.campaignType!)
        : null;
    return CampaignByOutlet(
      campaignType: campaignType,
      actionRefId: actionRefId,
      image: serverBannerUrl,
    );
  }
}

@JsonSerializable()
class CategoryItemResponse {
  final String id;

  @JsonKey(name: 'server_image_url')
  final String? serverImageUrl;

  final String name;

  CategoryItemResponse({
    required this.id,
    required this.serverImageUrl,
    required this.name,
  });

  factory CategoryItemResponse.fromJson(Map<String, dynamic> json) =>
      _$CategoryItemResponseFromJson(json);

  Map<String, dynamic> toJson() => _$CategoryItemResponseToJson(this);

  CategoryItem toCategoryItem() {
    return CategoryItem(id: id, image: serverImageUrl, name: name);
  }
}

@JsonSerializable()
class MainOutletResponse {
  final String uuid;

  final String name;

  @JsonKey(name: 'phone_number')
  final String? phoneNumber;

  @JsonKey(name: 'full_address')
  final String? location;

  @JsonKey(fromJson: toDouble)
  final double? latitude;

  @JsonKey(fromJson: toDouble)
  final double? longitude;

  const MainOutletResponse({
    required this.uuid,
    required this.name,
    required this.phoneNumber,
    required this.location,
    required this.latitude,
    required this.longitude,
  });

  factory MainOutletResponse.fromJson(Map<String, dynamic> json) =>
      _$MainOutletResponseFromJson(json);

  Map<String, dynamic> toJson() => _$MainOutletResponseToJson(this);

  MainOutlet toMainOutlet() {
    return MainOutlet(
      uuid: uuid,
      name: name,
      phoneNumber: phoneNumber,
      location: location,
      latitude: latitude,
      longitude: longitude,
    );
  }
}

@JsonSerializable()
class OtherOutletResponse {
  final String id;

  @JsonKey(name: 'server_banner_url')
  final String? serverBannerUrl;

  final String name;

  @JsonKey(name: 'phone_number')
  final String? phoneNumber;

  final String? location;

  @JsonKey(fromJson: toDouble)
  final double? latitude;

  @JsonKey(fromJson: toDouble)
  final double? longitude;

  @JsonKey(name: 'is_active')
  final bool isActive;

  OtherOutletResponse({
    required this.id,
    required this.serverBannerUrl,
    required this.name,
    required this.phoneNumber,
    required this.location,
    required this.latitude,
    required this.longitude,
    required this.isActive,
  });

  factory OtherOutletResponse.fromJson(Map<String, dynamic> json) =>
      _$OtherOutletResponseFromJson(json);

  Map<String, dynamic> toJson() => _$OtherOutletResponseToJson(this);

  OtherOutlet toOtherOutlet() {
    LatLng? coordinate;

    if (latitude != null && longitude != null) {
      coordinate = LatLng(latitude!, longitude!);
    }
    return OtherOutlet(
      id: id,
      image: serverBannerUrl,
      name: name,
      phoneNumber: phoneNumber,
      location: location,
      coordinate: coordinate,
      isActive: isActive,
    );
  }
}

@JsonSerializable()
class ProductItemResponse {
  final String id;

  @JsonKey(name: 'server_image_url')
  final String? serverImageUrl;

  final String name;
  @JsonKey(name: 'total_sold', fromJson: toDouble)
  final double totalSold;

  @JsonKey(name: 'average_rating', fromJson: toDouble)
  final double averageRating;

  @JsonKey(name: 'selling_price', fromJson: toDouble)
  final double sellingPrice;

  final int likes;

  ProductItemResponse({
    required this.id,
    required this.serverImageUrl,
    required this.name,
    required this.totalSold,
    required this.averageRating,
    required this.sellingPrice,
    required this.likes,
  });

  factory ProductItemResponse.fromJson(Map<String, dynamic> json) =>
      _$ProductItemResponseFromJson(json);

  Map<String, dynamic> toJson() => _$ProductItemResponseToJson(this);

  ProductItem toProductItem() {
    return ProductItem(
      id: id,
      image: serverImageUrl,
      name: name,
      totalSold: totalSold,
      averageRating: averageRating,
      sellingPrice: sellingPrice,
      likes: likes,
    );
  }
}
