package serviceparts

import (
	"strings"
	"system-altrak/internal/domain"
	"system-altrak/internal/repository"

	"gorm.io/gorm"
)

type ServicePartsRepository interface {
	Create(parts *domain.MemoServiceParts) error
	Update(parts *domain.MemoServiceParts, branchID uint, role string) error
	GetByID(id uint, branchID uint, role string) (*domain.MemoServiceParts, error)
	List(branchID uint, role string) ([]domain.MemoServiceParts, error)
	GetNextSequence(docType string, year, month int) (int, error)
	Delete(id uint, branchID uint, role string) error
}

type repositoryImpl struct {
	base *repository.Repository
}

func NewRepository(base *repository.Repository) ServicePartsRepository {
	return &repositoryImpl{base: base}
}

func (r *repositoryImpl) Create(parts *domain.MemoServiceParts) error {
	return r.base.GetDB().Transaction(func(tx *gorm.DB) error {
		// Save the main record first, omitting associations to handle them manually.
		// This bypasses GORM's automatic association saving which can cause PK conflicts.
		if err := tx.Omit("Checklist", "Equipments").Create(parts).Error; err != nil {
			return err
		}

		// 2. Save the checklist manually, ensuring ID is 0 for auto-increment.
		if parts.Checklist != nil {
			// Hapus dulu jika ada sampah (safety measure)
			if err := tx.Exec("DELETE FROM memo_service_parts_checklists WHERE memo_service_parts_id = ?", parts.ID).Error; err != nil {
				return err
			}
			parts.Checklist.ID = 0
			parts.Checklist.MemoServicePartsID = parts.ID
			// Gunakan 'id' (kecil) sesuai nama kolom di database untuk memastikan GORM tidak mengirimkannya
			if err := tx.Debug().Omit("id").Create(parts.Checklist).Error; err != nil {
				return err
			}
		}

		// 3. Save each equipment manually
		if err := tx.Exec("DELETE FROM memo_service_parts_equipments WHERE memo_service_parts_id = ?", parts.ID).Error; err != nil {
			return err
		}
		for i := range parts.Equipments {
			parts.Equipments[i].ID = 0
			parts.Equipments[i].MemoServicePartsID = parts.ID
			if err := tx.Omit("id").Create(&parts.Equipments[i]).Error; err != nil {
				return err
			}
		}

		return nil
	})
}

func (r *repositoryImpl) Update(parts *domain.MemoServiceParts, branchID uint, role string) error {
	return r.base.GetDB().Transaction(func(tx *gorm.DB) error {
		var existing domain.MemoServiceParts
		query := tx.Where("id = ?", parts.ID)
		if role != "superadmin" && branchID > 0 {
			query = query.Where("branch_id = ?", branchID)
		}
		if err := query.First(&existing).Error; err != nil {
			return err
		}

		status := existing.Status
		if strings.TrimSpace(parts.Status) != "" {
			status = parts.Status
		}

		if err := tx.Model(&existing).Updates(map[string]interface{}{
			"charge_to":          parts.ChargeTo,
			"cc":                 parts.CC,
			"customer":           parts.Customer,
			"equipment_location": parts.EquipmentLocation,
			"sales_agreement_no": parts.SalesAgreementNo,
			"description":        parts.Description,
			"status":             status,
			"engine_unit":        parts.EngineUnit,
			"esn_number":         parts.ESNNumber,
			"spv_name":           parts.SpvName,
			"spv_position":       parts.SpvPosition,
			"subject":            parts.Subject,
			"to":                 parts.To,
			"from":               parts.From,
		}).Error; err != nil {
			return err
		}

		// Use Unscoped().Delete() to permanently remove old associations before replacing them.
		// This prevents UNIQUE constraint issues with soft-deleted records.
		// Handle Checklist
		// Hapus secara fisik (Hard Delete) agar tidak ada konflik PK atau Unique Index
		if err := tx.Exec("DELETE FROM memo_service_parts_checklists WHERE memo_service_parts_id = ?", existing.ID).Error; err != nil {
			return err
		}
		
		if parts.Checklist != nil {
			parts.Checklist.ID = 0
			parts.Checklist.MemoServicePartsID = existing.ID
			if err := tx.Debug().Omit("id").Create(parts.Checklist).Error; err != nil {
				return err
			}
		}

		// Handle Equipments
		if err := tx.Exec("DELETE FROM memo_service_parts_equipments WHERE memo_service_parts_id = ?", existing.ID).Error; err != nil {
			return err
		}
		for i := range parts.Equipments {
			parts.Equipments[i].ID = 0
			parts.Equipments[i].MemoServicePartsID = existing.ID
			if err := tx.Omit("id").Create(&parts.Equipments[i]).Error; err != nil {
				return err
			}
		}
		return nil
	})
}

func (r *repositoryImpl) GetByID(id uint, branchID uint, role string) (*domain.MemoServiceParts, error) {
	var parts domain.MemoServiceParts
	q := r.base.GetDB().Preload("Checklist").Preload("Equipments")
	if role != "superadmin" && branchID > 0 {
		q = q.Where("branch_id = ?", branchID)
	}
	err := q.First(&parts, id).Error
	return &parts, err
}

func (r *repositoryImpl) List(branchID uint, role string) ([]domain.MemoServiceParts, error) {
	var list []domain.MemoServiceParts
	q := r.base.GetDB().Order("created_at desc")
	if role != "superadmin" && branchID > 0 {
		q = q.Where("branch_id = ?", branchID)
	}
	err := q.Find(&list).Error
	return list, err
}

func (r *repositoryImpl) GetNextSequence(docType string, year, month int) (int, error) {
	return r.base.GetNextSequence(docType, year, month)
}

func (r *repositoryImpl) Delete(id uint, branchID uint, role string) error {
	q := r.base.GetDB().Where("id = ?", id)
	if role != "superadmin" && branchID > 0 {
		q = q.Where("branch_id = ?", branchID)
	}
	return q.Delete(&domain.MemoServiceParts{}).Error
}
