package controllers

import (
	"strconv"
	"strings"

	"doormile/db"
	"doormile/dto"
	"doormile/models"
	"doormile/utils"

	"github.com/gofiber/fiber/v2"
)

func RegisterClient(c *fiber.Ctx) error {
	var input dto.CreateClientRequest
	if err := c.BodyParser(&input); err != nil {
		return utils.BadRequest(c, "invalid request body")
	}

	if input.FirstName == "" || input.Phone == "" {
		return utils.BadRequest(c, "first_name and phone are required")
	}

	if input.RegistrationSource == "" {
		input.RegistrationSource = DetermineSource(string(c.Request().Header.UserAgent()))
	}
	if input.DataConsent == "" {
		input.DataConsent = "full"
	}
	if input.Status == "" {
		input.Status = "newClient"
	}

	createAuth := input.Email != "" && input.Password != ""

	var hashedPassword string
	if createAuth {
		var err error
		hashedPassword, err = utils.HashPassword(input.Password)
		if err != nil {
			return utils.Internal(c, "failed to process registration")
		}
	}

	tx := db.DB.Begin()

	var existingClient models.DoormileClient
	if tx.Where("phone = ?", input.Phone).First(&existingClient).Error == nil {
		tx.Rollback()
		return utils.Conflict(c, "a client with this phone number already exists")
	}

	if createAuth {
		var existingAuth models.DoormileAuth
		if tx.Where("email = ?", input.Email).First(&existingAuth).Error == nil {
			tx.Rollback()
			return utils.Conflict(c, "this email address is already registered")
		}
	}

	client := models.DoormileClient{
		FirstName:          input.FirstName,
		LastName:           input.LastName,
		Phone:              input.Phone,
		Address:            input.Address,
		City:               input.City,
		State:              input.State,
		Neighbourhood:      input.Neighbourhood,
		Pincode:            input.Pincode,
		SurveyLat:          input.SurveyLat,
		SurveyLong:         input.SurveyLong,
		SurveyAddress:      input.SurveyAddress,
		SurveyZone:         input.SurveyZone,
		SurveyPincode:      input.SurveyPincode,
		BusinessType:       input.BusinessType,
		Status:             input.Status,
		ShippingFrequency:  input.ShippingFrequency,
		LogisticsSegment:   input.LogisticsSegment,
		TransitFrom:        input.TransitFrom,
		TransitTo:          input.TransitTo,
		DataConsent:        input.DataConsent,
		RegistrationSource: input.RegistrationSource,
		RegisteredByID:     input.RegisteredByID,
	}

	if input.DataConsent == "full" {
		client.ParcelVolume = input.ParcelVolume
		client.ActiveContracts = input.ActiveContracts
		client.LogisticsProvider = input.LogisticsProvider
		client.ProviderEfficiency = input.ProviderEfficiency
		client.Notes = input.Notes
	} else {
		client.ParcelVolume = 0
		client.ActiveContracts = 0
		client.LogisticsProvider = "Not disclosed"
		client.ProviderEfficiency = ""
		client.Notes = ""
	}

	if err := tx.Create(&client).Error; err != nil {
		tx.Rollback()
		return utils.Internal(c, "failed to create client")
	}

	email, role := "", ""
	if createAuth {
		auth := models.DoormileAuth{
			ClientID:     &client.ID,
			Email:        input.Email,
			PasswordHash: hashedPassword,
			Role:         "user",
		}
		if err := tx.Create(&auth).Error; err != nil {
			tx.Rollback()
			return utils.Internal(c, "failed to create auth credentials")
		}
		email = auth.Email
		role = auth.Role
	}

	tx.Commit()

	return utils.Created(c, buildClientResponse(client, email, role))
}

func GetClients(c *fiber.Ctx) error {
	var clients []models.DoormileClient
	if err := db.DB.Find(&clients).Error; err != nil {
		return utils.Internal(c, "failed to fetch clients")
	}

	// Bulk load auth records once and map by client_id for O(1) lookup
	var auths []models.DoormileAuth
	db.DB.Find(&auths)
	authByClientID := make(map[uint64]models.DoormileAuth, len(auths))
	for _, a := range auths {
		if a.ClientID != nil {
			authByClientID[*a.ClientID] = a
		}
	}

	responses := make([]dto.ClientResponse, 0, len(clients))
	for _, client := range clients {
		auth := authByClientID[client.ID]
		responses = append(responses, buildClientResponse(client, auth.Email, auth.Role))
	}

	return utils.List(c, responses, int64(len(responses)))
}

func GetClientDetails(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil || id == 0 {
		return utils.BadRequest(c, "invalid client ID")
	}

	var client models.DoormileClient
	if err := db.DB.First(&client, id).Error; err != nil {
		return utils.NotFound(c, "client not found")
	}

	var auth models.DoormileAuth
	email, role := "", ""
	if db.DB.Where("client_id = ?", id).First(&auth).Error == nil {
		email = auth.Email
		role = auth.Role
	}

	return utils.OK(c, buildClientResponse(client, email, role))
}

func UpdateClient(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil || id == 0 {
		return utils.BadRequest(c, "invalid client ID")
	}

	var client models.DoormileClient
	if err := db.DB.First(&client, id).Error; err != nil {
		return utils.NotFound(c, "client not found")
	}

	var input dto.CreateClientRequest
	if err := c.BodyParser(&input); err != nil {
		return utils.BadRequest(c, "invalid request body")
	}

	if input.FirstName != "" {
		client.FirstName = input.FirstName
	}
	if input.LastName != "" {
		client.LastName = input.LastName
	}
	if input.Phone != "" {
		client.Phone = input.Phone
	}
	if input.Address != "" {
		client.Address = input.Address
	}
	if input.City != "" {
		client.City = input.City
	}
	if input.State != "" {
		client.State = input.State
	}
	if input.Neighbourhood != "" {
		client.Neighbourhood = input.Neighbourhood
	}
	if input.Pincode != "" {
		client.Pincode = input.Pincode
	}
	if input.SurveyLat != 0 {
		client.SurveyLat = input.SurveyLat
	}
	if input.SurveyLong != 0 {
		client.SurveyLong = input.SurveyLong
	}
	if input.SurveyAddress != "" {
		client.SurveyAddress = input.SurveyAddress
	}
	if input.SurveyZone != "" {
		client.SurveyZone = input.SurveyZone
	}
	if input.SurveyPincode != "" {
		client.SurveyPincode = input.SurveyPincode
	}
	if input.BusinessType != "" {
		client.BusinessType = input.BusinessType
	}
	if input.Status != "" {
		client.Status = input.Status
	}
	if input.ShippingFrequency != "" {
		client.ShippingFrequency = input.ShippingFrequency
	}
	if input.LogisticsSegment != "" {
		client.LogisticsSegment = input.LogisticsSegment
	}
	if input.TransitFrom != "" {
		client.TransitFrom = input.TransitFrom
	}
	if input.TransitTo != "" {
		client.TransitTo = input.TransitTo
	}
	if input.RegistrationSource != "" {
		client.RegistrationSource = input.RegistrationSource
	}
	if input.RegisteredByID != 0 {
		client.RegisteredByID = input.RegisteredByID
	}
	client.Notes = input.Notes

	if input.DataConsent != "" {
		client.DataConsent = input.DataConsent
	}

	if client.DataConsent == "full" {
		client.ParcelVolume = input.ParcelVolume
		client.ActiveContracts = input.ActiveContracts
		client.LogisticsProvider = input.LogisticsProvider
		client.ProviderEfficiency = input.ProviderEfficiency
	} else {
		client.ParcelVolume = 0
		client.ActiveContracts = 0
		client.LogisticsProvider = "Not disclosed"
		client.ProviderEfficiency = ""
		client.Notes = ""
	}

	tx := db.DB.Begin()

	if err := tx.Save(&client).Error; err != nil {
		tx.Rollback()
		return utils.Internal(c, "failed to update client")
	}

	var auth models.DoormileAuth
	email, role := "", ""
	if db.DB.Where("client_id = ?", id).First(&auth).Error == nil {
		email = auth.Email
		role = auth.Role
	}

	authUpdated := false
	if input.Email != "" && input.Email != auth.Email {
		auth.Email = input.Email
		email = input.Email
		authUpdated = true
	}
	if input.Password != "" {
		hashed, err := utils.HashPassword(input.Password)
		if err != nil {
			tx.Rollback()
			return utils.Internal(c, "failed to process password update")
		}
		auth.PasswordHash = hashed
		authUpdated = true
	}
	if authUpdated && auth.ID != 0 {
		if err := tx.Save(&auth).Error; err != nil {
			tx.Rollback()
			return utils.Internal(c, "failed to update credentials")
		}
	}

	tx.Commit()

	return utils.OK(c, buildClientResponse(client, email, role))
}

func DeleteClient(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil || id == 0 {
		return utils.BadRequest(c, "invalid client ID")
	}

	var client models.DoormileClient
	if err := db.DB.First(&client, id).Error; err != nil {
		return utils.NotFound(c, "client not found")
	}

	tx := db.DB.Begin()

	if err := tx.Where("client_id = ?", id).Delete(&models.DoormileAuth{}).Error; err != nil {
		tx.Rollback()
		return utils.Internal(c, "failed to delete client credentials")
	}

	if err := tx.Delete(&client).Error; err != nil {
		tx.Rollback()
		return utils.Internal(c, "failed to delete client")
	}

	tx.Commit()

	return utils.Message(c, "client deleted successfully")
}

func buildClientResponse(client models.DoormileClient, email, role string) dto.ClientResponse {
	return dto.ClientResponse{
		ID:                 client.ID,
		CreatedAt:          client.CreatedAt.Format("2006-01-02T15:04:05Z"),
		LastUpdated:        client.UpdatedAt.Format("2006-01-02"),
		FirstName:          client.FirstName,
		LastName:           client.LastName,
		Email:              email,
		Phone:              client.Phone,
		Address:            client.Address,
		City:               client.City,
		State:              client.State,
		Neighbourhood:      client.Neighbourhood,
		Pincode:            client.Pincode,
		SurveyLat:          client.SurveyLat,
		SurveyLong:         client.SurveyLong,
		SurveyAddress:      client.SurveyAddress,
		SurveyZone:         client.SurveyZone,
		SurveyPincode:      client.SurveyPincode,
		BusinessType:       client.BusinessType,
		Status:             client.Status,
		ShippingFrequency:  client.ShippingFrequency,
		LogisticsSegment:   client.LogisticsSegment,
		TransitFrom:        client.TransitFrom,
		TransitTo:          client.TransitTo,
		ParcelVolume:       client.ParcelVolume,
		ActiveContracts:    client.ActiveContracts,
		LogisticsProvider:  client.LogisticsProvider,
		ProviderEfficiency: client.ProviderEfficiency,
		Notes:              client.Notes,
		DataConsent:        client.DataConsent,
		RegistrationSource: client.RegistrationSource,
		RegisteredByID:     client.RegisteredByID,
		Role:               role,
	}
}

func DetermineSource(userAgent string) string {
	ua := strings.ToLower(userAgent)
	if strings.Contains(ua, "dart") || strings.Contains(ua, "flutter") || strings.Contains(ua, "doormile") {
		return "mobile"
	}
	if strings.Contains(ua, "mozilla") || strings.Contains(ua, "chrome") || strings.Contains(ua, "safari") {
		return "web"
	}
	return "api_tool"
}
