package services

import (
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"rijig/dto"
	"rijig/internal/repositories"
	"rijig/model"
	"rijig/utils"
)

type WilayahIndonesiaService interface {
	ImportDataFromCSV() error

	GetAllProvinces(page, limit int) ([]dto.ProvinceResponseDTO, int, error)
	GetProvinceByID(id string, page, limit int) (*dto.ProvinceResponseDTO, int, error)

	GetAllRegencies(page, limit int) ([]dto.RegencyResponseDTO, int, error)
	GetRegencyByID(id string, page, limit int) (*dto.RegencyResponseDTO, int, error)

	GetAllDistricts(page, limit int) ([]dto.DistrictResponseDTO, int, error)
	GetDistrictByID(id string, page, limit int) (*dto.DistrictResponseDTO, int, error)

	GetAllVillages(page, limit int) ([]dto.VillageResponseDTO, int, error)
	GetVillageByID(id string) (*dto.VillageResponseDTO, error)
}

type wilayahIndonesiaService struct {
	WilayahRepo repositories.WilayahIndonesiaRepository
}

func NewWilayahIndonesiaService(wilayahRepo repositories.WilayahIndonesiaRepository) WilayahIndonesiaService {
	return &wilayahIndonesiaService{WilayahRepo: wilayahRepo}
}

func (s *wilayahIndonesiaService) ImportDataFromCSV() error {

	provinces, err := utils.ReadCSV("public/document/provinces.csv")
	if err != nil {
		return fmt.Errorf("failed to read provinces CSV: %v", err)
	}

	var provinceList []model.Province
	for _, record := range provinces[1:] {
		province := model.Province{
			ID:   record[0],
			Name: record[1],
		}
		provinceList = append(provinceList, province)
	}

	if err := s.WilayahRepo.ImportProvinces(provinceList); err != nil {
		return fmt.Errorf("failed to import provinces: %v", err)
	}

	regencies, err := utils.ReadCSV("public/document/regencies.csv")
	if err != nil {
		return fmt.Errorf("failed to read regencies CSV: %v", err)
	}

	var regencyList []model.Regency
	for _, record := range regencies[1:] {
		regency := model.Regency{
			ID:         record[0],
			ProvinceID: record[1],
			Name:       record[2],
		}
		regencyList = append(regencyList, regency)
	}

	if err := s.WilayahRepo.ImportRegencies(regencyList); err != nil {
		return fmt.Errorf("failed to import regencies: %v", err)
	}

	districts, err := utils.ReadCSV("public/document/districts.csv")
	if err != nil {
		return fmt.Errorf("failed to read districts CSV: %v", err)
	}

	var districtList []model.District
	for _, record := range districts[1:] {
		district := model.District{
			ID:        record[0],
			RegencyID: record[1],
			Name:      record[2],
		}
		districtList = append(districtList, district)
	}

	if err := s.WilayahRepo.ImportDistricts(districtList); err != nil {
		return fmt.Errorf("failed to import districts: %v", err)
	}

	villages, err := utils.ReadCSV("public/document/villages.csv")
	if err != nil {
		return fmt.Errorf("failed to read villages CSV: %v", err)
	}

	var villageList []model.Village
	for _, record := range villages[1:] {
		village := model.Village{
			ID:         record[0],
			DistrictID: record[1],
			Name:       record[2],
		}
		villageList = append(villageList, village)
	}

	if err := s.WilayahRepo.ImportVillages(villageList); err != nil {
		return fmt.Errorf("failed to import villages: %v", err)
	}

	return nil
}

func (s *wilayahIndonesiaService) GetAllProvinces(page, limit int) ([]dto.ProvinceResponseDTO, int, error) {

	cacheKey := fmt.Sprintf("provinces_page:%d_limit:%d", page, limit)
	cachedData, err := utils.GetJSONData(cacheKey)
	if err == nil && cachedData != nil {
		var provinces []dto.ProvinceResponseDTO
		if data, ok := cachedData["data"].([]interface{}); ok {
			for _, item := range data {
				province, ok := item.(map[string]interface{})
				if ok {
					provinces = append(provinces, dto.ProvinceResponseDTO{
						ID:   province["id"].(string),
						Name: province["name"].(string),
					})
				}
			}
			total := int(cachedData["total"].(float64))
			return provinces, total, nil
		}
	}

	provinces, total, err := s.WilayahRepo.FindAllProvinces(page, limit)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to fetch provinces: %v", err)
	}

	var provinceDTOs []dto.ProvinceResponseDTO
	for _, province := range provinces {
		provinceDTOs = append(provinceDTOs, dto.ProvinceResponseDTO{
			ID:   province.ID,
			Name: province.Name,
		})
	}

	cacheData := map[string]interface{}{
		"data":  provinceDTOs,
		"total": total,
	}
	err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
	if err != nil {
		fmt.Printf("Error caching provinces data: %v\n", err)
	}

	return provinceDTOs, total, nil
}

func (s *wilayahIndonesiaService) GetProvinceByID(id string, page, limit int) (*dto.ProvinceResponseDTO, int, error) {

	cacheKey := fmt.Sprintf("province:%s_page:%d_limit:%d", id, page, limit)
	cachedData, err := utils.GetJSONData(cacheKey)
	if err == nil && cachedData != nil {

		var provinceDTO dto.ProvinceResponseDTO
		if data, ok := cachedData["data"].(string); ok {
			if err := json.Unmarshal([]byte(data), &provinceDTO); err == nil {

				totalRegencies, _ := strconv.Atoi(cachedData["total_regencies"].(string))
				return &provinceDTO, totalRegencies, nil
			}
		}
	}

	province, totalRegencies, err := s.WilayahRepo.FindProvinceByID(id, page, limit)
	if err != nil {
		return nil, 0, err
	}

	provinceDTO := dto.ProvinceResponseDTO{
		ID:   province.ID,
		Name: province.Name,
	}

	var regencyDTOs []dto.RegencyResponseDTO
	for _, regency := range province.Regencies {
		regencyDTO := dto.RegencyResponseDTO{
			ID:         regency.ID,
			ProvinceID: regency.ProvinceID,
			Name:       regency.Name,
		}
		regencyDTOs = append(regencyDTOs, regencyDTO)
	}

	provinceDTO.Regencies = regencyDTOs

	cacheData := map[string]interface{}{
		"data":            provinceDTO,
		"total_regencies": strconv.Itoa(totalRegencies),
	}
	err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
	if err != nil {
		fmt.Printf("Error caching province data: %v\n", err)
	}

	return &provinceDTO, totalRegencies, nil
}

func (s *wilayahIndonesiaService) GetAllRegencies(page, limit int) ([]dto.RegencyResponseDTO, int, error) {

	cacheKey := fmt.Sprintf("regencies_page:%d_limit:%d", page, limit)
	cachedData, err := utils.GetJSONData(cacheKey)
	if err == nil && cachedData != nil {
		var regencies []dto.RegencyResponseDTO
		if data, ok := cachedData["data"].([]interface{}); ok {
			for _, item := range data {
				regency, ok := item.(map[string]interface{})
				if ok {
					regencies = append(regencies, dto.RegencyResponseDTO{
						ID:         regency["id"].(string),
						ProvinceID: regency["province_id"].(string),
						Name:       regency["name"].(string),
					})
				}
			}
			total := int(cachedData["total"].(float64))
			return regencies, total, nil
		}
	}

	regencies, total, err := s.WilayahRepo.FindAllRegencies(page, limit)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to fetch provinces: %v", err)
	}

	var regencyDTOs []dto.RegencyResponseDTO
	for _, regency := range regencies {
		regencyDTOs = append(regencyDTOs, dto.RegencyResponseDTO{
			ID:         regency.ID,
			ProvinceID: regency.ProvinceID,
			Name:       regency.Name,
		})
	}

	cacheData := map[string]interface{}{
		"data":  regencyDTOs,
		"total": total,
	}
	err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
	if err != nil {
		fmt.Printf("Error caching regencies data: %v\n", err)
	}

	return regencyDTOs, total, nil
}

func (s *wilayahIndonesiaService) GetRegencyByID(id string, page, limit int) (*dto.RegencyResponseDTO, int, error) {

	cacheKey := fmt.Sprintf("regency:%s_page:%d_limit:%d", id, page, limit)
	cachedData, err := utils.GetJSONData(cacheKey)
	if err == nil && cachedData != nil {

		var regencyDTO dto.RegencyResponseDTO
		if data, ok := cachedData["data"].(string); ok {
			if err := json.Unmarshal([]byte(data), &regencyDTO); err == nil {

				totalDistrict, _ := strconv.Atoi(cachedData["total_regencies"].(string))
				return &regencyDTO, totalDistrict, nil
			}
		}
	}

	regency, totalDistrict, err := s.WilayahRepo.FindRegencyByID(id, page, limit)
	if err != nil {
		return nil, 0, err
	}

	regencyDTO := dto.RegencyResponseDTO{
		ID:         regency.ID,
		ProvinceID: regency.ProvinceID,
		Name:       regency.Name,
	}

	var districtDTOs []dto.DistrictResponseDTO
	for _, regency := range regency.Districts {
		districtDTO := dto.DistrictResponseDTO{
			ID:        regency.ID,
			RegencyID: regency.RegencyID,
			Name:      regency.Name,
		}
		districtDTOs = append(districtDTOs, districtDTO)
	}

	regencyDTO.Districts = districtDTOs

	cacheData := map[string]interface{}{
		"data":            regencyDTO,
		"total_regencies": strconv.Itoa(totalDistrict),
	}
	err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
	if err != nil {
		fmt.Printf("Error caching province data: %v\n", err)
	}

	return &regencyDTO, totalDistrict, nil
}

func (s *wilayahIndonesiaService) GetAllDistricts(page, limit int) ([]dto.DistrictResponseDTO, int, error) {

	cacheKey := fmt.Sprintf("district_page:%d_limit:%d", page, limit)
	cachedData, err := utils.GetJSONData(cacheKey)
	if err == nil && cachedData != nil {
		var districts []dto.DistrictResponseDTO
		if data, ok := cachedData["data"].([]interface{}); ok {
			for _, item := range data {
				district, ok := item.(map[string]interface{})
				if ok {
					districts = append(districts, dto.DistrictResponseDTO{
						ID:        district["id"].(string),
						RegencyID: district["regency_id"].(string),
						Name:      district["name"].(string),
					})
				}
			}
			total := int(cachedData["total"].(float64))
			return districts, total, nil
		}
	}

	districts, total, err := s.WilayahRepo.FindAllDistricts(page, limit)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to fetch districts: %v", err)
	}

	var districtsDTOs []dto.DistrictResponseDTO
	for _, district := range districts {
		districtsDTOs = append(districtsDTOs, dto.DistrictResponseDTO{
			ID:        district.ID,
			RegencyID: district.RegencyID,
			Name:      district.Name,
		})
	}

	cacheData := map[string]interface{}{
		"data":  districtsDTOs,
		"total": total,
	}
	err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
	if err != nil {
		fmt.Printf("Error caching districts data: %v\n", err)
	}

	return districtsDTOs, total, nil
}

func (s *wilayahIndonesiaService) GetDistrictByID(id string, page, limit int) (*dto.DistrictResponseDTO, int, error) {

	cacheKey := fmt.Sprintf("district:%s_page:%d_limit:%d", id, page, limit)
	cachedData, err := utils.GetJSONData(cacheKey)
	if err == nil && cachedData != nil {

		var districtDTO dto.DistrictResponseDTO
		if data, ok := cachedData["data"].(string); ok {
			if err := json.Unmarshal([]byte(data), &districtDTO); err == nil {

				totalVillage, _ := strconv.Atoi(cachedData["total_village"].(string))
				return &districtDTO, totalVillage, nil
			}
		}
	}

	district, totalVillages, err := s.WilayahRepo.FindDistrictByID(id, page, limit)
	if err != nil {
		return nil, 0, err
	}

	districtDTO := dto.DistrictResponseDTO{
		ID:        district.ID,
		RegencyID: district.RegencyID,
		Name:      district.Name,
	}

	var villageDTOs []dto.VillageResponseDTO
	for _, village := range district.Villages {
		regencyDTO := dto.VillageResponseDTO{
			ID:         village.ID,
			DistrictID: village.DistrictID,
			Name:       village.Name,
		}
		villageDTOs = append(villageDTOs, regencyDTO)
	}

	districtDTO.Villages = villageDTOs

	cacheData := map[string]interface{}{
		"data":           districtDTO,
		"total_villages": strconv.Itoa(totalVillages),
	}
	err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
	if err != nil {
		fmt.Printf("Error caching province data: %v\n", err)
	}

	return &districtDTO, totalVillages, nil
}

func (s *wilayahIndonesiaService) GetAllVillages(page, limit int) ([]dto.VillageResponseDTO, int, error) {

	cacheKey := fmt.Sprintf("villages_page:%d_limit:%d", page, limit)

	cachedData, err := utils.GetJSONData(cacheKey)
	if err == nil && cachedData != nil {
		var villages []dto.VillageResponseDTO
		if data, ok := cachedData["data"].([]interface{}); ok {
			for _, item := range data {
				villageData, ok := item.(map[string]interface{})
				if ok {
					villages = append(villages, dto.VillageResponseDTO{
						ID:         villageData["id"].(string),
						DistrictID: villageData["district_id"].(string),
						Name:       villageData["name"].(string),
					})
				}
			}
			return villages, int(cachedData["total"].(float64)), nil
		}
	}

	villages, total, err := s.WilayahRepo.FindAllVillages(page, limit)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to fetch villages: %v", err)
	}

	var villageDTOs []dto.VillageResponseDTO
	for _, village := range villages {
		villageDTOs = append(villageDTOs, dto.VillageResponseDTO{
			ID:         village.ID,
			DistrictID: village.DistrictID,
			Name:       village.Name,
		})
	}

	cacheData := map[string]interface{}{
		"data":  villageDTOs,
		"total": total,
	}
	err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
	if err != nil {
		fmt.Printf("Error caching villages data to Redis: %v\n", err)
	}

	return villageDTOs, total, nil
}

func (s *wilayahIndonesiaService) GetVillageByID(id string) (*dto.VillageResponseDTO, error) {

	cacheKey := fmt.Sprintf("village:%s", id)
	cachedData, err := utils.GetJSONData(cacheKey)
	if err == nil && cachedData != nil {
		villageResponse := &dto.VillageResponseDTO{}
		if data, ok := cachedData["data"].(string); ok {
			if err := json.Unmarshal([]byte(data), villageResponse); err == nil {
				return villageResponse, nil
			}
		}
	}

	village, err := s.WilayahRepo.FindVillageByID(id)
	if err != nil {
		return nil, fmt.Errorf("village not found: %v", err)
	}

	villageResponse := &dto.VillageResponseDTO{
		ID:         village.ID,
		DistrictID: village.DistrictID,
		Name:       village.Name,
	}

	cacheData := map[string]interface{}{
		"data": villageResponse,
	}
	err = utils.SetJSONData(cacheKey, cacheData, 24*time.Hour)
	if err != nil {
		fmt.Printf("Error caching village data to Redis: %v\n", err)
	}

	return villageResponse, nil
}
