package activity

import (
	"fmt"
	"strings"
	"time"

	"system-altrak/internal/domain"

	"gorm.io/gorm"
)

type ActivityRepository interface {
	List(branchID uint, role string) ([]domain.ActivityLog, error)
	Dashboard(branchID uint, role string) (*DashboardResponse, error)
	Restore(resource string, id uint, branchID uint, role string) error
	Log(activity *domain.ActivityLog) error
}

type repositoryImpl struct {
	db *gorm.DB
}

func NewRepository(db *gorm.DB) ActivityRepository {
	return &repositoryImpl{db: db}
}

func (r *repositoryImpl) List(branchID uint, role string) ([]domain.ActivityLog, error) {
	logs, err := r.fetchActivityLogs(branchID, role)
	if err != nil {
		return nil, err
	}

	for i := range logs {
		NormalizeActivityLog(&logs[i])
	}

	return logs, nil
}

func (r *repositoryImpl) Dashboard(branchID uint, role string) (*DashboardResponse, error) {
	logs, err := r.fetchActivityLogs(branchID, role)
	if err != nil {
		return nil, err
	}

	for i := range logs {
		NormalizeActivityLog(&logs[i])
	}

	cards := buildModuleCards(logs)
	deleted, err := r.fetchDeletedRecords(branchID, role)
	if err != nil {
		return nil, err
	}

	return &DashboardResponse{
		Cards:        cards,
		Logs:         logs,
		Deleted:      deleted,
		TotalLogs:    len(logs),
		TotalDeleted: len(deleted),
	}, nil
}

func (r *repositoryImpl) Restore(resource string, id uint, branchID uint, role string) error {
	switch resolveResourceKey(resource) {
	case ResourceKeyOperationalRecord:
		return r.restoreOperationalRecord(id, branchID, role)
	case ResourceKeyCustomerProfile:
		return r.restoreCustomerProfile(id, branchID, role)
	case ResourceKeyMemorandum:
		return r.restoreMemorandum(id, branchID, role)
	case ResourceKeyCreditLimit:
		return r.restoreCreditLimit(id, branchID, role)
	case ResourceKeyServiceJobIOM:
		return r.restoreServiceJobIOM(id, branchID, role)
	case ResourceKeyServiceRequisition:
		return r.restoreServiceRequisition(id, branchID, role)
	case ResourceKeyUserAccount:
		return r.restoreUser(id, branchID, role)
	default:
		return fmt.Errorf("unsupported recovery resource %q", resource)
	}
}

func (r *repositoryImpl) Log(activity *domain.ActivityLog) error {
	return r.db.Create(activity).Error
}

func (r *repositoryImpl) fetchActivityLogs(branchID uint, role string) ([]domain.ActivityLog, error) {
	var logs []domain.ActivityLog
	q := r.db.Order("created_at desc")
	if role != "superadmin" && branchID > 0 {
		q = q.Where("branch_id = ?", branchID)
	}
	if err := q.Find(&logs).Error; err != nil {
		return nil, err
	}

	return logs, nil
}

func buildModuleCards(logs []domain.ActivityLog) []ModuleCard {
	index := make(map[string]*ModuleCard)
	order := []string{
		ModuleKeyPOStatus,
		ModuleKeyCustomerProfile,
		ModuleKeyIOMManagement,
		ModuleKeyISRManagement,
		ModuleKeyUserAccounts,
		ModuleKeyAuthentication,
		ModuleKeyAuditLog,
		ModuleKeyCreditLimit,
		ModuleKeyServiceJobIOM,
		ModuleKeyUnknown,
	}

	for _, log := range logs {
		moduleKey := ResolveModuleKeyFromLog(log)
		card, exists := index[moduleKey]
		if !exists {
			card = &ModuleCard{ModuleKey: moduleKey, ModuleLabel: ResolveModuleLabel(moduleKey)}
			index[moduleKey] = card
		}
		card.Total++
		if card.LatestAt == nil {
			latestAt := log.CreatedAt
			card.LatestAt = &latestAt
			card.LatestAction = log.Action
			card.LatestActor = log.Username
		}
	}

	cards := make([]ModuleCard, 0, len(index)+len(order))
	seen := make(map[string]struct{}, len(order))
	for _, key := range order {
		seen[key] = struct{}{}
		if card, exists := index[key]; exists {
			cards = append(cards, *card)
			continue
		}
		cards = append(cards, ModuleCard{ModuleKey: key, ModuleLabel: ResolveModuleLabel(key)})
	}

	for key, card := range index {
		if _, exists := seen[key]; exists {
			continue
		}
		cards = append(cards, *card)
	}

	return cards
}

func resolveResourceKey(raw string) string {
	normalized := strings.ToLower(strings.TrimSpace(raw))
	normalized = strings.ReplaceAll(normalized, "-", "_")
	normalized = strings.ReplaceAll(normalized, " ", "_")
	switch normalized {
	case ResourceKeyOperationalRecord, "pso_status", "pso", "po_status":
		return ResourceKeyOperationalRecord
	case ResourceKeyCustomerProfile, "customerprofile":
		return ResourceKeyCustomerProfile
	case ResourceKeyMemorandum, "memo", "iom":
		return ResourceKeyMemorandum
	case ResourceKeyCreditLimit, "creditlimit":
		return ResourceKeyCreditLimit
	case ResourceKeyServiceJobIOM, "sjr":
		return ResourceKeyServiceJobIOM
	case ResourceKeyServiceRequisition, "sr":
		return ResourceKeyServiceRequisition
	case ResourceKeyUserAccount, "user", "users", "user_accounts", "admin_users":
		return ResourceKeyUserAccount
	default:
		return normalized
	}
}

func applyBranchFilter(q *gorm.DB, branchID uint, role string) *gorm.DB {
	if role != "superadmin" && branchID > 0 {
		return q.Where("branch_id = ?", branchID)
	}
	return q
}

func deletedAtOrNow(deletedAt gorm.DeletedAt) time.Time {
	if deletedAt.Valid {
		return deletedAt.Time
	}
	return time.Now()
}

func firstNonEmpty(values ...string) string {
	for _, value := range values {
		if strings.TrimSpace(value) != "" {
			return strings.TrimSpace(value)
		}
	}
	return "-"
}

func (r *repositoryImpl) fetchDeletedRecords(branchID uint, role string) ([]DeletedRecord, error) {
	records := make([]DeletedRecord, 0)

	appendRecords := func(items []DeletedRecord) {
		records = append(records, items...)
	}

	if items, err := r.deletedOperationalRecords(branchID, role); err != nil {
		return nil, err
	} else {
		appendRecords(items)
	}
	if items, err := r.deletedCustomerProfiles(branchID, role); err != nil {
		return nil, err
	} else {
		appendRecords(items)
	}
	if items, err := r.deletedMemorandums(branchID, role); err != nil {
		return nil, err
	} else {
		appendRecords(items)
	}
	if items, err := r.deletedCreditLimits(branchID, role); err != nil {
		return nil, err
	} else {
		appendRecords(items)
	}
	if items, err := r.deletedServiceJobIOM(branchID, role); err != nil {
		return nil, err
	} else {
		appendRecords(items)
	}
	if items, err := r.deletedServiceRequisitions(branchID, role); err != nil {
		return nil, err
	} else {
		appendRecords(items)
	}
	if items, err := r.deletedUsers(branchID, role); err != nil {
		return nil, err
	} else {
		appendRecords(items)
	}

	return records, nil
}

func (r *repositoryImpl) deletedOperationalRecords(branchID uint, role string) ([]DeletedRecord, error) {
	var list []domain.OperationalRecord
	q := r.db.Unscoped().Where("deleted_at IS NOT NULL")
	q = applyBranchFilter(q, branchID, role).Order("deleted_at desc")
	if err := q.Find(&list).Error; err != nil {
		return nil, err
	}

	items := make([]DeletedRecord, 0, len(list))
	for _, record := range list {
		items = append(items, DeletedRecord{
			ModuleKey:     ModuleKeyPOStatus,
			ModuleLabel:   ResolveModuleLabel(ModuleKeyPOStatus),
			ResourceKey:   ResourceKeyOperationalRecord,
			ResourceLabel: "Operational Record",
			ID:            record.ID,
			Title:         firstNonEmpty(record.PsoNo, record.CustomerName, record.PoNo),
			Subtitle:      firstNonEmpty(record.CustomerName, record.PoNo),
			BranchID:      record.BranchID,
			DeletedAt:     deletedAtOrNow(record.DeletedAt),
			Restorable:    true,
		})
	}

	return items, nil
}

func (r *repositoryImpl) deletedCustomerProfiles(branchID uint, role string) ([]DeletedRecord, error) {
	var list []domain.CustomerProfile
	q := r.db.Unscoped().Where("deleted_at IS NOT NULL")
	q = applyBranchFilter(q, branchID, role).Order("deleted_at desc")
	if err := q.Find(&list).Error; err != nil {
		return nil, err
	}

	items := make([]DeletedRecord, 0, len(list))
	for _, record := range list {
		items = append(items, DeletedRecord{
			ModuleKey:     ModuleKeyCustomerProfile,
			ModuleLabel:   ResolveModuleLabel(ModuleKeyCustomerProfile),
			ResourceKey:   ResourceKeyCustomerProfile,
			ResourceLabel: "Customer Profile",
			ID:            record.ID,
			Title:         firstNonEmpty(record.CustomerName, record.DocumentNumber),
			Subtitle:      firstNonEmpty(record.DocumentNumber, record.CityBilling),
			BranchID:      record.BranchID,
			DeletedAt:     deletedAtOrNow(record.DeletedAt),
			Restorable:    true,
		})
	}

	return items, nil
}

func (r *repositoryImpl) deletedMemorandums(branchID uint, role string) ([]DeletedRecord, error) {
	var list []domain.Memorandum
	q := applyBranchFilter(r.db.Unscoped().Where("deleted_at IS NOT NULL"), branchID, role).Order("deleted_at desc")
	if err := q.Find(&list).Error; err != nil {
		return nil, err
	}

	items := make([]DeletedRecord, 0, len(list))
	for _, record := range list {
		items = append(items, DeletedRecord{
			ModuleKey:     ModuleKeyIOMManagement,
			ModuleLabel:   ResolveModuleLabel(ModuleKeyIOMManagement),
			ResourceKey:   ResourceKeyMemorandum,
			ResourceLabel: "IOM Memorandum",
			ID:            record.ID,
			Title:         firstNonEmpty(record.Subject, record.Ref),
			Subtitle:      firstNonEmpty(record.Ref, record.ToDepartment),
			BranchID:      record.BranchID,
			DeletedAt:     deletedAtOrNow(record.DeletedAt),
			Restorable:    true,
		})
	}

	return items, nil
}

func (r *repositoryImpl) deletedCreditLimits(branchID uint, role string) ([]DeletedRecord, error) {
	var list []domain.CreditLimit
	q := applyBranchFilter(r.db.Unscoped().Where("deleted_at IS NOT NULL"), branchID, role).Order("deleted_at desc")
	if err := q.Find(&list).Error; err != nil {
		return nil, err
	}

	items := make([]DeletedRecord, 0, len(list))
	for _, record := range list {
		items = append(items, DeletedRecord{
			ModuleKey:     ModuleKeyIOMManagement,
			ModuleLabel:   ResolveModuleLabel(ModuleKeyIOMManagement),
			ResourceKey:   ResourceKeyCreditLimit,
			ResourceLabel: "Credit Limit",
			ID:            record.ID,
			Title:         firstNonEmpty(record.Ref, record.Customer),
			Subtitle:      firstNonEmpty(record.Customer, record.CustomerCode),
			BranchID:      record.BranchID,
			DeletedAt:     deletedAtOrNow(record.DeletedAt),
			Restorable:    true,
		})
	}

	return items, nil
}

func (r *repositoryImpl) deletedServiceJobIOM(branchID uint, role string) ([]DeletedRecord, error) {
	var list []domain.ServiceAuthorization
	q := applyBranchFilter(r.db.Unscoped().Where("deleted_at IS NOT NULL"), branchID, role).Order("deleted_at desc")
	if err := q.Find(&list).Error; err != nil {
		return nil, err
	}

	items := make([]DeletedRecord, 0, len(list))
	for _, record := range list {
		items = append(items, DeletedRecord{
			ModuleKey:     ModuleKeyIOMManagement,
			ModuleLabel:   ResolveModuleLabel(ModuleKeyIOMManagement),
			ResourceKey:   ResourceKeyServiceJobIOM,
			ResourceLabel: "Service Job IOM",
			ID:            record.ID,
			Title:         firstNonEmpty(record.Ref, record.CustomerName),
			Subtitle:      firstNonEmpty(record.JobName, record.EngineModel, record.ESN),
			BranchID:      record.BranchID,
			DeletedAt:     deletedAtOrNow(record.DeletedAt),
			Restorable:    true,
		})
	}

	return items, nil
}

func (r *repositoryImpl) deletedServiceRequisitions(branchID uint, role string) ([]DeletedRecord, error) {
	var list []domain.ServiceRequisition
	q := applyBranchFilter(r.db.Unscoped().Where("deleted_at IS NOT NULL"), branchID, role).Order("deleted_at desc")
	if err := q.Find(&list).Error; err != nil {
		return nil, err
	}

	items := make([]DeletedRecord, 0, len(list))
	for _, record := range list {
		items = append(items, DeletedRecord{
			ModuleKey:     ModuleKeyISRManagement,
			ModuleLabel:   ResolveModuleLabel(ModuleKeyISRManagement),
			ResourceKey:   ResourceKeyServiceRequisition,
			ResourceLabel: "Service Requisition",
			ID:            record.ID,
			Title:         firstNonEmpty(record.Ref, record.Customer),
			Subtitle:      firstNonEmpty(record.Customer, record.Status),
			BranchID:      record.BranchID,
			DeletedAt:     deletedAtOrNow(record.DeletedAt),
			Restorable:    true,
		})
	}

	return items, nil
}

func (r *repositoryImpl) deletedUsers(branchID uint, role string) ([]DeletedRecord, error) {
	var list []domain.User
	q := applyBranchFilter(r.db.Unscoped().Where("deleted_at IS NOT NULL"), branchID, role).Order("deleted_at desc")
	if err := q.Find(&list).Error; err != nil {
		return nil, err
	}

	items := make([]DeletedRecord, 0, len(list))
	for _, record := range list {
		items = append(items, DeletedRecord{
			ModuleKey:     ModuleKeyUserAccounts,
			ModuleLabel:   ResolveModuleLabel(ModuleKeyUserAccounts),
			ResourceKey:   ResourceKeyUserAccount,
			ResourceLabel: "User Account",
			ID:            record.ID,
			Title:         firstNonEmpty(record.Username, record.FullName),
			Subtitle:      firstNonEmpty(record.FullName, record.Role),
			BranchID:      record.BranchID,
			DeletedAt:     deletedAtOrNow(record.DeletedAt),
			Restorable:    true,
		})
	}

	return items, nil
}

func (r *repositoryImpl) restoreOperationalRecord(id, branchID uint, role string) error {
	return r.db.Transaction(func(tx *gorm.DB) error {
		var record domain.OperationalRecord
		q := tx.Unscoped().Where("id = ? AND deleted_at IS NOT NULL", id)
		if role != "superadmin" && branchID > 0 {
			q = q.Where("branch_id = ?", branchID)
		}
		if err := q.First(&record).Error; err != nil {
			return err
		}
		return tx.Unscoped().Model(&record).Update("deleted_at", nil).Error
	})
}

func (r *repositoryImpl) restoreCustomerProfile(id, branchID uint, role string) error {
	return r.db.Transaction(func(tx *gorm.DB) error {
		var profile domain.CustomerProfile
		q := tx.Unscoped().Where("id = ? AND deleted_at IS NOT NULL", id)
		if role != "superadmin" && branchID > 0 {
			q = q.Where("branch_id = ?", branchID)
		}
		if err := q.First(&profile).Error; err != nil {
			return err
		}

		if err := tx.Unscoped().Model(&profile).Update("deleted_at", nil).Error; err != nil {
			return err
		}
		if err := tx.Unscoped().Model(&domain.CustomerDecisionMaker{}).Where("user_profile_id = ?", profile.ID).Update("deleted_at", nil).Error; err != nil {
			return err
		}
		if err := tx.Unscoped().Model(&domain.CustomerEquipment{}).Where("user_profile_id = ?", profile.ID).Update("deleted_at", nil).Error; err != nil {
			return err
		}
		if err := tx.Unscoped().Model(&domain.CustomerAffiliation{}).Where("user_profile_id = ?", profile.ID).Update("deleted_at", nil).Error; err != nil {
			return err
		}

		return nil
	})
}

func (r *repositoryImpl) restoreMemorandum(id, branchID uint, role string) error {
	return r.db.Transaction(func(tx *gorm.DB) error {
		var memo domain.Memorandum
		q := tx.Unscoped().Where("id = ? AND deleted_at IS NOT NULL", id)
		if role != "superadmin" && branchID > 0 {
			q = q.Where("branch_id = ?", branchID)
		}
		if err := q.First(&memo).Error; err != nil {
			return err
		}
		return tx.Unscoped().Model(&memo).Update("deleted_at", nil).Error
	})
}

func (r *repositoryImpl) restoreCreditLimit(id, branchID uint, role string) error {
	return r.db.Transaction(func(tx *gorm.DB) error {
		var record domain.CreditLimit
		q := tx.Unscoped().Where("id = ? AND deleted_at IS NOT NULL", id)
		if role != "superadmin" && branchID > 0 {
			q = q.Where("branch_id = ?", branchID)
		}
		if err := q.First(&record).Error; err != nil {
			return err
		}
		return tx.Unscoped().Model(&record).Update("deleted_at", nil).Error
	})
}

func (r *repositoryImpl) restoreServiceJobIOM(id, branchID uint, role string) error {
	return r.db.Transaction(func(tx *gorm.DB) error {
		var record domain.ServiceAuthorization
		q := tx.Unscoped().Where("id = ? AND deleted_at IS NOT NULL", id)
		if role != "superadmin" && branchID > 0 {
			q = q.Where("branch_id = ?", branchID)
		}
		if err := q.First(&record).Error; err != nil {
			return err
		}
		return tx.Unscoped().Model(&record).Update("deleted_at", nil).Error
	})
}

func (r *repositoryImpl) restoreServiceRequisition(id, branchID uint, role string) error {
	return r.db.Transaction(func(tx *gorm.DB) error {
		var sr domain.ServiceRequisition
		q := tx.Unscoped().Where("id = ? AND deleted_at IS NOT NULL", id)
		if role != "superadmin" && branchID > 0 {
			q = q.Where("branch_id = ?", branchID)
		}
		if err := q.First(&sr).Error; err != nil {
			return err
		}

		if err := tx.Unscoped().Model(&sr).Update("deleted_at", nil).Error; err != nil {
			return err
		}
		if err := tx.Unscoped().Model(&domain.ServiceRequisitionChecklist{}).Where("service_requisition_id = ?", sr.ID).Update("deleted_at", nil).Error; err != nil {
			return err
		}
		if err := tx.Unscoped().Model(&domain.ServiceRequisitionEquipment{}).Where("service_requisition_id = ?", sr.ID).Update("deleted_at", nil).Error; err != nil {
			return err
		}

		return nil
	})
}

func (r *repositoryImpl) restoreUser(id, branchID uint, role string) error {
	return r.db.Transaction(func(tx *gorm.DB) error {
		var user domain.User
		q := tx.Unscoped().Where("id = ? AND deleted_at IS NOT NULL", id)
		if role != "superadmin" && branchID > 0 {
			q = q.Where("branch_id = ?", branchID)
		}
		if err := q.First(&user).Error; err != nil {
			return err
		}
		return tx.Unscoped().Model(&user).Update("deleted_at", nil).Error
	})
}
