package postal_code

import (
	"cargo-erp-backend/internal/domain"
	"cargo-erp-backend/pkg/helpers"
	"cargo-erp-backend/pkg/importer"
	"fmt"
	"strings"
	"time"

	"gorm.io/gorm"
)

type PostalCodeImportHandler struct {
	UserData              domain.User
	Helper                helpers.HelperInterface
	lastProvinceName      string
	lastCityName          string
	lastDistrictName      string
	lastSubdistrictName   string
}

func NewPostalCodeImportHandler(userData domain.User, helper helpers.HelperInterface) importer.ImportHandler {
	return &PostalCodeImportHandler{
		UserData: userData,
		Helper:   helper,
	}
}

func (h *PostalCodeImportHandler) ValidateRow(row []string, rowNumber int) (importer.RowData, []error) {
	var errors []error

	if len(row) < 5 {
		errors = append(errors, fmt.Errorf("row must have 5 columns: postal_code, subdistrict_name, district_name, city_name, province_name"))
		return nil, errors
	}

	postalCode := strings.TrimSpace(row[0])
	if postalCode == "" {
		errors = append(errors, fmt.Errorf("postal_code is required"))
	} else if len(postalCode) > 10 {
		errors = append(errors, fmt.Errorf("postal_code must be at most 10 characters"))
	}

	subdistrictName := strings.TrimSpace(row[1])
	if subdistrictName == "" {
		errors = append(errors, fmt.Errorf("subdistrict_name is required"))
	}

	districtName := strings.TrimSpace(row[2])
	if districtName == "" {
		errors = append(errors, fmt.Errorf("district_name is required"))
	}

	cityName := strings.TrimSpace(row[3])
	if cityName == "" {
		errors = append(errors, fmt.Errorf("city_name is required"))
	}

	provinceName := strings.TrimSpace(row[4])
	if provinceName == "" {
		errors = append(errors, fmt.Errorf("province_name is required"))
	}

	if len(errors) > 0 {
		return nil, errors
	}

	var province domain.Province
	db := h.Helper.GetDB("slave")
	if err := db.Where("UPPER(name) = ?", strings.ToUpper(provinceName)).First(&province).Error; err != nil {
		errors = append(errors, fmt.Errorf("province '%s' not found", provinceName))
		return nil, errors
	}

	var city domain.City
	if err := db.Where("UPPER(name) = ? AND province_id = ?", strings.ToUpper(cityName), province.ID).First(&city).Error; err != nil {
		errors = append(errors, fmt.Errorf("city '%s' not found in province '%s'", cityName, provinceName))
		return nil, errors
	}

	var district domain.District
	if err := db.Where("UPPER(name) = ? AND city_id = ?", strings.ToUpper(districtName), city.ID).First(&district).Error; err != nil {
		errors = append(errors, fmt.Errorf("district '%s' not found in city '%s'", districtName, cityName))
		return nil, errors
	}

	var subdistrict domain.Subdistrict
	if err := db.Where("UPPER(name) = ? AND district_id = ?", strings.ToUpper(subdistrictName), district.ID).First(&subdistrict).Error; err != nil {
		errors = append(errors, fmt.Errorf("subdistrict '%s' not found in district '%s'", subdistrictName, districtName))
		return nil, errors
	}

	h.lastProvinceName = provinceName
	h.lastCityName = cityName
	h.lastDistrictName = districtName
	h.lastSubdistrictName = subdistrictName

	now := time.Now()
	nowStr := now.Format("2006-01-02 15:04:05.999999-07:00")
	pc := &domain.PostalCode{
		PostalCode:    &postalCode,
		ProvinceID:    &province.ID,
		CityID:        &city.ID,
		DistrictID:    &district.ID,
		SubdistrictID: &subdistrict.ID,
		MerchantID:    h.UserData.MerchantID,
		CreatedBy:     &h.UserData.ID,
		CreatedOn:     &now,
		UpdatedBy:     &h.UserData.ID,
		UpdatedOn:     &nowStr,
	}

	return pc, nil
}

func (h *PostalCodeImportHandler) GetDataString(data importer.RowData) string {
	pc := data.(*domain.PostalCode)
	code := ""
	if pc.PostalCode != nil {
		code = *pc.PostalCode
	}
	return fmt.Sprintf("%s, %s, %s, %s, %s", code, h.lastSubdistrictName, h.lastDistrictName, h.lastCityName, h.lastProvinceName)
}

func (h *PostalCodeImportHandler) IsDuplicate(db *gorm.DB, data importer.RowData) bool {
	pc := data.(*domain.PostalCode)
	var count int64
	db.Model(&domain.PostalCode{}).
		Joins("JOIN subdistrict ON postal_code.subdistrict_id = subdistrict.id").
		Joins("JOIN district ON postal_code.district_id = district.id").
		Joins("JOIN city ON postal_code.city_id = city.id").
		Joins("JOIN province ON postal_code.province_id = province.id").
		Where("UPPER(postal_code.postal_code) = ? AND UPPER(subdistrict.name) = ? AND UPPER(district.name) = ? AND UPPER(city.name) = ? AND UPPER(province.name) = ?",
			strings.ToUpper(*pc.PostalCode), strings.ToUpper(h.lastSubdistrictName), strings.ToUpper(h.lastDistrictName), strings.ToUpper(h.lastCityName), strings.ToUpper(h.lastProvinceName)).
		Count(&count)
	return count > 0
}

func (h *PostalCodeImportHandler) GetTemplateHeaders() []string {
	return []string{"postal_code", "subdistrict_name", "district_name", "city_name", "province_name"}
}

func (h *PostalCodeImportHandler) GetTemplateRows() [][]string {
	return [][]string{
		{"10430", "Kramat Pulo", "Jakarta Pusat", "Jakarta Pusat", "DKI Jakarta"},
		{"10430", "Senen", "Jakarta Pusat", "Jakarta Pusat", "DKI Jakarta"},
		{"10440", "Cempaka Putih", "Jakarta Pusat", "Jakarta Pusat", "DKI Jakarta"},
		{"10510", "Gambir", "Jakarta Pusat", "Jakarta Pusat", "DKI Jakarta"},
	}
}
