package region

import (
	"context"
	"database/sql"
	"errors"
	"strings"

	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	userdomain "hyapp/services/user-service/internal/domain/user"
	"hyapp/services/user-service/internal/storage/mysql/shared"

	mysqldriver "github.com/go-sql-driver/mysql"
)

func (r *Repository) ResolveEnabledCountryByCode(ctx context.Context, countryCode string) (userdomain.Country, bool, error) {
	return resolveCountryByCode(ctx, r.db, countryCode, true)
}

// ListRegistrationCountries 返回注册页可选国家，只包含 enabled 的国家。

func (r *Repository) ListRegistrationCountries(ctx context.Context) ([]userdomain.Country, error) {
	enabled := true
	return listCountries(ctx, r.db, userdomain.CountryFilter{Enabled: &enabled})
}

// ResolveActiveRegionByCountry 按国家码读取当前 active 区域映射；无显式映射时由 regions.go 返回 GLOBAL。

// UpdateCountry 修改国家展示字段；country_code 不允许原地修改。

func (r *Repository) UpdateCountry(ctx context.Context, command userdomain.UpdateCountryCommand) (userdomain.Country, error) {
	result, err := r.db.ExecContext(ctx, `
		UPDATE countries
		SET country_name = ?,
			iso_alpha3 = ?,
			iso_numeric = ?,
			country_display_name = ?,
			phone_country_code = ?,
			flag = ?,
			sort_order = ?,
			updated_by_user_id = ?,
			updated_at_ms = ?
		WHERE app_code = ? AND country_id = ?
	`, command.CountryName, shared.NullableString(command.ISOAlpha3), shared.NullableString(command.ISONumeric), command.CountryDisplayName, shared.NullableString(command.PhoneCountryCode), command.Flag, command.SortOrder, shared.NullableOperator(command.OperatorUserID), command.NowMs, appcode.Normalize(command.AppCode), command.CountryID)
	if err != nil {
		return userdomain.Country{}, mapCountryDuplicateError(err)
	}
	if affected, err := result.RowsAffected(); err != nil {
		return userdomain.Country{}, err
	} else if affected == 0 {
		return userdomain.Country{}, xerr.New(xerr.CountryNotFound, "country not found")
	}

	return queryCountry(ctx, r.db, "WHERE app_code = ? AND country_id = ?", appcode.Normalize(command.AppCode), command.CountryID)
}

// ListCountries 返回国家主数据，enabled 为空时返回全部。

func (r *Repository) ListCountries(ctx context.Context, filter userdomain.CountryFilter) ([]userdomain.Country, error) {
	return listCountries(ctx, r.db, filter)
}

func queryCountry(ctx context.Context, q queryer, clause string, args ...any) (userdomain.Country, error) {
	row := q.QueryRowContext(ctx, `
		SELECT app_code, country_id, country_name, country_code,
		       COALESCE(iso_alpha3, ''), COALESCE(iso_numeric, ''),
		       country_display_name, COALESCE(phone_country_code, ''), COALESCE(flag, ''), enabled,
		       sort_order,
		       COALESCE(created_by_user_id, 0), COALESCE(updated_by_user_id, 0), created_at_ms, updated_at_ms
		FROM countries
		`+clause, args...)
	return scanCountry(row)
}

func listCountries(ctx context.Context, q queryer, filter userdomain.CountryFilter) ([]userdomain.Country, error) {
	appCode := appcode.FromContext(ctx)
	query := `
		SELECT app_code, country_id, country_name, country_code,
		       COALESCE(iso_alpha3, ''), COALESCE(iso_numeric, ''),
		       country_display_name, COALESCE(phone_country_code, ''), COALESCE(flag, ''), enabled,
		       sort_order,
		       COALESCE(created_by_user_id, 0), COALESCE(updated_by_user_id, 0), created_at_ms, updated_at_ms
		FROM countries`
	args := []any{appCode}
	conditions := []string{"app_code = ?"}
	if filter.Enabled != nil {
		conditions = append(conditions, "enabled = ?")
		args = append(args, *filter.Enabled)
	}
	if len(conditions) > 0 {
		query += " WHERE " + strings.Join(conditions, " AND ")
	}
	query += " ORDER BY sort_order DESC, country_code ASC"
	rows, err := queryRows(ctx, q, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	countries := []userdomain.Country{}
	for rows.Next() {
		country, err := scanCountry(rows)
		if err != nil {
			return nil, err
		}
		countries = append(countries, country)
	}

	return countries, rows.Err()
}

func scanCountry(scanner interface{ Scan(dest ...any) error }) (userdomain.Country, error) {
	var country userdomain.Country
	err := scanner.Scan(
		&country.AppCode,
		&country.CountryID,
		&country.CountryName,
		&country.CountryCode,
		&country.ISOAlpha3,
		&country.ISONumeric,
		&country.CountryDisplayName,
		&country.PhoneCountryCode,
		&country.Flag,
		&country.Enabled,
		&country.SortOrder,
		&country.CreatedByUserID,
		&country.UpdatedByUserID,
		&country.CreatedAtMs,
		&country.UpdatedAtMs,
	)
	return country, err
}

func canonicalCountryCodes(ctx context.Context, q queryer, countries []string) ([]string, error) {
	seen := make(map[string]struct{}, len(countries))
	result := make([]string, 0, len(countries))
	for _, input := range countries {
		country, ok, err := resolveCountryByCode(ctx, q, input, false)
		if err != nil {
			return nil, err
		}
		if !ok {
			return nil, xerr.New(xerr.InvalidArgument, "country is not supported")
		}
		if _, exists := seen[country.CountryCode]; exists {
			// 区域国家列表不能重复，避免重复写入 active 国家归属。
			return nil, xerr.New(xerr.InvalidArgument, "countries must not contain duplicate canonical codes")
		}
		seen[country.CountryCode] = struct{}{}
		result = append(result, country.CountryCode)
	}

	return result, nil
}

func resolveCountryByCode(ctx context.Context, q queryer, countryCode string, requireEnabled bool) (userdomain.Country, bool, error) {
	countryCode = userdomain.NormalizeCountryCode(countryCode)
	clause := "WHERE app_code = ? AND country_code = ?"
	args := []any{appcode.FromContext(ctx), countryCode}
	if requireEnabled {
		clause += " AND enabled = ?"
		args = append(args, true)
	}
	country, err := queryCountry(ctx, q, clause, args...)
	if err == sql.ErrNoRows {
		return userdomain.Country{}, false, nil
	}
	if err != nil {
		return userdomain.Country{}, false, err
	}

	return country, true, nil
}

func ensureCountriesAssignableToRegion(ctx context.Context, q queryer, countries []string, regionID int64) error {
	for _, country := range countries {
		mappedRegionID, mapped, err := activeRegionIDByCountry(ctx, q, country)
		if err != nil {
			return err
		}
		if mapped && mappedRegionID != regionID {
			return xerr.New(xerr.RegionCountryConflict, "country already belongs to another active region")
		}
	}

	return nil
}

func activeRegionIDByCountry(ctx context.Context, q queryer, countryCode string) (int64, bool, error) {
	var regionID int64
	err := q.QueryRowContext(ctx, `
		SELECT region_id
		FROM region_countries
		WHERE app_code = ? AND country_code = ? AND status = ?
		LIMIT 1
	`, appcode.FromContext(ctx), countryCode, userdomain.RegionStatusActive).Scan(&regionID)
	if err == sql.ErrNoRows {
		return 0, false, nil
	}
	if err != nil {
		return 0, false, err
	}

	return regionID, true, nil
}

func mapCountryDuplicateError(err error) error {
	var mysqlErr *mysqldriver.MySQLError
	if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 {
		return xerr.New(xerr.Conflict, "country_code already exists")
	}
	return err
}

func mapRegionCountryDuplicateError(err error) error {
	var mysqlErr *mysqldriver.MySQLError
	if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 {
		return xerr.New(xerr.RegionCountryConflict, "country already belongs to another active region")
	}
	return err
}
