package dashboard

import (
	"fmt"
	"strings"
	"system-altrak/internal/domain"
	"system-altrak/internal/repository"
	"time"
)

type DashboardRepository interface {
	GetStats(branchID uint, role string) (map[string]interface{}, error)
	GetAlerts(branchID uint, role string) ([]map[string]interface{}, error)
}

type repositoryImpl struct {
	base *repository.Repository
}

type dashboardTrendPoint struct {
	Date      string `json:"date"`
	Label     string `json:"label"`
	Documents int    `json:"documents"`
	Verified  int    `json:"verified"`
}

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

func (r *repositoryImpl) GetStats(branchID uint, role string) (map[string]interface{}, error) {
	var activeCustomers int64
	var pendingRecords int64
	var verifiedRecords int64
	var totalRecords int64

	db := r.base.GetDB()
	normalizedRole := strings.ToLower(strings.TrimSpace(role))

	qCust := db.Model(&domain.CustomerProfile{})
	qPending := db.Model(&domain.OperationalRecord{}).Where("is_verified = ?", false)
	qVerified := db.Model(&domain.OperationalRecord{}).Where("is_verified = ?", true)
	qTotal := db.Model(&domain.OperationalRecord{})
	qTop := db.Model(&domain.OperationalRecord{})

	if normalizedRole != "superadmin" && branchID > 0 {
		qCust = qCust.Where("branch_id = ?", branchID)
		qPending = qPending.Where("branch_id = ?", branchID)
		qVerified = qVerified.Where("branch_id = ?", branchID)
		qTotal = qTotal.Where("branch_id = ?", branchID)
		qTop = qTop.Where("branch_id = ?", branchID)
	}

	if err := qCust.Count(&activeCustomers).Error; err != nil {
		return nil, fmt.Errorf("failed to count customer profiles: %w", err)
	}
	if err := qPending.Count(&pendingRecords).Error; err != nil {
		return nil, fmt.Errorf("failed to count pending operational records: %w", err)
	}
	if err := qVerified.Count(&verifiedRecords).Error; err != nil {
		return nil, fmt.Errorf("failed to count verified operational records: %w", err)
	}
	if err := qTotal.Count(&totalRecords).Error; err != nil {
		return nil, fmt.Errorf("failed to count total operational records: %w", err)
	}

	// Top customers based on document volume
	poOverview := []struct {
		CustomerName string `json:"customer_name"`
		Count        int    `json:"count"`
	}{}
	if err := qTop.Select("customer_name, count(*) as count").
		Group("customer_name").
		Order("count desc").
		Limit(5).
		Scan(&poOverview).Error; err != nil {
		return nil, fmt.Errorf("failed to load top customers: %w", err)
	}

	now := time.Now().Local()
	startDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).AddDate(0, 0, -29)
	trendRows := make([]struct {
		CreatedAt  time.Time  `gorm:"column:created_at"`
		VerifiedAt *time.Time `gorm:"column:verified_at"`
	}, 0)

	qTrend := db.Model(&domain.OperationalRecord{}).Select("created_at, verified_at")
	if normalizedRole != "superadmin" && branchID > 0 {
		qTrend = qTrend.Where("branch_id = ?", branchID)
	}

	if err := qTrend.Where("created_at >= ? OR (verified_at IS NOT NULL AND verified_at >= ?)", startDate, startDate).
		Order("created_at asc").
		Find(&trendRows).Error; err != nil {
		return nil, fmt.Errorf("failed to load dashboard trend: %w", err)
	}

	trendIndex := make(map[string]*dashboardTrendPoint, 30)
	for i := 0; i < 30; i++ {
		day := startDate.AddDate(0, 0, i)
		key := day.Format("2006-01-02")
		trendIndex[key] = &dashboardTrendPoint{
			Date:  key,
			Label: day.Format("02 Jan"),
		}
	}

	for _, row := range trendRows {
		createdKey := row.CreatedAt.In(now.Location()).Format("2006-01-02")
		if point, ok := trendIndex[createdKey]; ok {
			point.Documents++
		}

		if row.VerifiedAt != nil {
			verifiedKey := row.VerifiedAt.In(now.Location()).Format("2006-01-02")
			if point, ok := trendIndex[verifiedKey]; ok {
				point.Verified++
			}
		}
	}

	activityTrend := make([]dashboardTrendPoint, 0, 30)
	for i := 0; i < 30; i++ {
		day := startDate.AddDate(0, 0, i)
		activityTrend = append(activityTrend, *trendIndex[day.Format("2006-01-02")])
	}

	return map[string]interface{}{
		"active_customers": activeCustomers,
		"pending_docs":     pendingRecords,
		"verified_docs":    verifiedRecords,
		"total_docs":       totalRecords,
		"po_overview":      poOverview,
		"activity_trend":   activityTrend,
	}, nil
}

func (r *repositoryImpl) GetAlerts(branchID uint, role string) ([]map[string]interface{}, error) {
	db := r.base.GetDB()
	normalizedRole := strings.ToLower(strings.TrimSpace(role))

	// Find records that are not verified and overdue based on their TOP value (Term of Payment)
	// calculated from PsoDate (Tanggal Dokument).
	qAlerts := db.Model(&domain.OperationalRecord{}).
		Select("id, customer_name, pso_no, po_no, pso_date, created_at, top").
		Where("is_verified = ?", false)
	if normalizedRole != "superadmin" && branchID > 0 {
		qAlerts = qAlerts.Where("branch_id = ?", branchID)
	}

	var records []domain.OperationalRecord
	if err := qAlerts.Order("created_at asc").Limit(20).Find(&records).Error; err != nil {
		return nil, fmt.Errorf("failed to fetch alerts: %w", err)
	}

	now := time.Now()
	alerts := make([]map[string]interface{}, 0, len(records))
	for _, rec := range records {
		// Use PsoDate (Tanggal Dokument) as the base date. Fallback to CreatedAt if nil.
		baseDate := rec.CreatedAt
		if rec.PsoDate != nil {
			baseDate = *rec.PsoDate
		}

		dueDate := baseDate.AddDate(0, 0, int(rec.TOP))
		if now.After(dueDate) {
			daysOverdue := int(now.Sub(dueDate).Hours() / 24)
			
			poDisplay := rec.PoNo
			if poDisplay == "" {
				poDisplay = rec.PsoNo
			}

			// Peringatan: No PO [poDisplay] Sudah melewati [rec.TOP] Hari
			warningMsg := fmt.Sprintf("No PO %s Sudah melewati %d Hari", poDisplay, rec.TOP)

			alerts = append(alerts, map[string]interface{}{
				"id":            rec.ID,
				"customer_name": rec.CustomerName,
				"document_type": "PSO",
				"pso_no":        rec.PsoNo,
				"po_no":         rec.PoNo,
				"pso_date":      rec.PsoDate,
				"created_at":    rec.CreatedAt,
				"top":           rec.TOP,
				"days_overdue":  daysOverdue,
				"po_display":    poDisplay,
				"warning_msg":   warningMsg,
			})
		}
	}

	return alerts, nil
}
