package processor

import (
	"context"
	"fmt"
	"time"

	"apskel-pos-be/internal/models"
	"apskel-pos-be/internal/repository"
)

type AnalyticsProcessor interface {
	GetPaymentMethodAnalytics(ctx context.Context, req *models.PaymentMethodAnalyticsRequest) (*models.PaymentMethodAnalyticsResponse, error)
	GetSalesAnalytics(ctx context.Context, req *models.SalesAnalyticsRequest) (*models.SalesAnalyticsResponse, error)
	GetProductAnalytics(ctx context.Context, req *models.ProductAnalyticsRequest) (*models.ProductAnalyticsResponse, error)
	GetProductAnalyticsPerCategory(ctx context.Context, req *models.ProductAnalyticsPerCategoryRequest) (*models.ProductAnalyticsPerCategoryResponse, error)
	GetDashboardAnalytics(ctx context.Context, req *models.DashboardAnalyticsRequest) (*models.DashboardAnalyticsResponse, error)
	GetProfitLossAnalytics(ctx context.Context, req *models.ProfitLossAnalyticsRequest) (*models.ProfitLossAnalyticsResponse, error)
}

type AnalyticsProcessorImpl struct {
	analyticsRepo repository.AnalyticsRepository
}

func NewAnalyticsProcessorImpl(analyticsRepo repository.AnalyticsRepository) *AnalyticsProcessorImpl {
	return &AnalyticsProcessorImpl{
		analyticsRepo: analyticsRepo,
	}
}

func (p *AnalyticsProcessorImpl) GetPaymentMethodAnalytics(ctx context.Context, req *models.PaymentMethodAnalyticsRequest) (*models.PaymentMethodAnalyticsResponse, error) {
	if req.DateFrom.After(req.DateTo) {
		return nil, fmt.Errorf("date_from cannot be after date_to")
	}

	analyticsData, err := p.analyticsRepo.GetPaymentMethodAnalytics(ctx, req.OrganizationID, req.OutletID, req.DateFrom, req.DateTo)
	if err != nil {
		return nil, fmt.Errorf("failed to get payment method analytics: %w", err)
	}

	var totalAmount float64
	var totalOrders int64
	var totalPayments int64

	for _, data := range analyticsData {
		totalAmount += data.TotalAmount
		totalOrders += data.OrderCount
		totalPayments += data.PaymentCount
	}

	var averageOrderValue float64
	if totalOrders > 0 {
		averageOrderValue = totalAmount / float64(totalOrders)
	}

	// Calculate percentages
	var resultData []models.PaymentMethodAnalyticsData
	for _, data := range analyticsData {
		var percentage float64
		if totalAmount > 0 {
			percentage = (data.TotalAmount / totalAmount) * 100
		}

		resultData = append(resultData, models.PaymentMethodAnalyticsData{
			PaymentMethodID:   data.PaymentMethodID,
			PaymentMethodName: data.PaymentMethodName,
			PaymentMethodType: data.PaymentMethodType,
			TotalAmount:       data.TotalAmount,
			OrderCount:        data.OrderCount,
			PaymentCount:      data.PaymentCount,
			Percentage:        percentage,
		})
	}

	summary := models.PaymentMethodSummary{
		TotalAmount:       totalAmount,
		TotalOrders:       totalOrders,
		TotalPayments:     totalPayments,
		AverageOrderValue: averageOrderValue,
	}

	return &models.PaymentMethodAnalyticsResponse{
		OrganizationID: req.OrganizationID,
		OutletID:       req.OutletID,
		DateFrom:       req.DateFrom,
		DateTo:         req.DateTo,
		GroupBy:        req.GroupBy,
		Summary:        summary,
		Data:           resultData,
	}, nil
}

func (p *AnalyticsProcessorImpl) GetSalesAnalytics(ctx context.Context, req *models.SalesAnalyticsRequest) (*models.SalesAnalyticsResponse, error) {
	// Validate date range
	if req.DateFrom.After(req.DateTo) {
		return nil, fmt.Errorf("date_from cannot be after date_to")
	}

	// Validate groupBy
	if req.GroupBy == "" {
		req.GroupBy = "day"
	}

	// Get analytics data from repository
	analyticsData, err := p.analyticsRepo.GetSalesAnalytics(ctx, req.OrganizationID, req.OutletID, req.DateFrom, req.DateTo, req.GroupBy)
	if err != nil {
		return nil, fmt.Errorf("failed to get sales analytics: %w", err)
	}

	// Calculate summary
	var totalSales float64
	var totalOrders int64
	var totalItems int64
	var totalTax float64
	var totalDiscount float64
	var netSales float64

	for _, data := range analyticsData {
		totalSales += data.Sales
		totalOrders += data.Orders
		totalItems += data.Items
		totalTax += data.Tax
		totalDiscount += data.Discount
		netSales += data.NetSales
	}

	var averageOrderValue float64
	if totalOrders > 0 {
		averageOrderValue = totalSales / float64(totalOrders)
	}

	// Transform data
	var resultData []models.SalesAnalyticsData
	for _, data := range analyticsData {
		resultData = append(resultData, models.SalesAnalyticsData{
			Date:     data.Date,
			Sales:    data.Sales,
			Orders:   data.Orders,
			Items:    data.Items,
			Tax:      data.Tax,
			Discount: data.Discount,
			NetSales: data.NetSales,
		})
	}

	summary := models.SalesSummary{
		TotalSales:        totalSales,
		TotalOrders:       totalOrders,
		TotalItems:        totalItems,
		AverageOrderValue: averageOrderValue,
		TotalTax:          totalTax,
		TotalDiscount:     totalDiscount,
		NetSales:          netSales,
	}

	return &models.SalesAnalyticsResponse{
		OrganizationID: req.OrganizationID,
		OutletID:       req.OutletID,
		DateFrom:       req.DateFrom,
		DateTo:         req.DateTo,
		GroupBy:        req.GroupBy,
		Summary:        summary,
		Data:           resultData,
	}, nil
}

func (p *AnalyticsProcessorImpl) GetProductAnalytics(ctx context.Context, req *models.ProductAnalyticsRequest) (*models.ProductAnalyticsResponse, error) {
	// Validate date range
	if req.DateFrom.After(req.DateTo) {
		return nil, fmt.Errorf("date_from cannot be after date_to")
	}

	// Set default limit
	if req.Limit <= 0 {
		req.Limit = 10
	}

	// Get analytics data from repository
	analyticsData, err := p.analyticsRepo.GetProductAnalytics(ctx, req.OrganizationID, req.OutletID, req.DateFrom, req.DateTo, req.Limit)
	if err != nil {
		return nil, fmt.Errorf("failed to get product analytics: %w", err)
	}

	// Transform data
	var resultData []models.ProductAnalyticsData
	for _, data := range analyticsData {
		resultData = append(resultData, models.ProductAnalyticsData{
			ProductID:    data.ProductID,
			ProductName:  data.ProductName,
			CategoryID:   data.CategoryID,
			CategoryName: data.CategoryName,
			QuantitySold: data.QuantitySold,
			Revenue:      data.Revenue,
			AveragePrice: data.AveragePrice,
			OrderCount:   data.OrderCount,
		})
	}

	return &models.ProductAnalyticsResponse{
		OrganizationID: req.OrganizationID,
		OutletID:       req.OutletID,
		DateFrom:       req.DateFrom,
		DateTo:         req.DateTo,
		Data:           resultData,
	}, nil
}

func (p *AnalyticsProcessorImpl) GetProductAnalyticsPerCategory(ctx context.Context, req *models.ProductAnalyticsPerCategoryRequest) (*models.ProductAnalyticsPerCategoryResponse, error) {
	// Validate date range
	if req.DateFrom.After(req.DateTo) {
		return nil, fmt.Errorf("date_from cannot be after date_to")
	}

	// Get analytics data from repository
	analyticsData, err := p.analyticsRepo.GetProductAnalyticsPerCategory(ctx, req.OrganizationID, req.OutletID, req.DateFrom, req.DateTo)
	if err != nil {
		return nil, fmt.Errorf("failed to get product analytics per category: %w", err)
	}

	// Transform data
	var resultData []models.ProductAnalyticsPerCategoryData
	for _, data := range analyticsData {
		resultData = append(resultData, models.ProductAnalyticsPerCategoryData{
			CategoryID:   data.CategoryID,
			CategoryName: data.CategoryName,
			TotalRevenue: data.TotalRevenue,
			TotalQuantity: data.TotalQuantity,
			ProductCount: data.ProductCount,
			OrderCount:   data.OrderCount,
		})
	}

	return &models.ProductAnalyticsPerCategoryResponse{
		OrganizationID: req.OrganizationID,
		OutletID:       req.OutletID,
		DateFrom:       req.DateFrom,
		DateTo:         req.DateTo,
		Data:           resultData,
	}, nil
}

func (p *AnalyticsProcessorImpl) GetDashboardAnalytics(ctx context.Context, req *models.DashboardAnalyticsRequest) (*models.DashboardAnalyticsResponse, error) {
	// Validate date range
	if req.DateFrom.After(req.DateTo) {
		return nil, fmt.Errorf("date_from cannot be after date_to")
	}

	// Get dashboard overview
	overview, err := p.analyticsRepo.GetDashboardOverview(ctx, req.OrganizationID, req.OutletID, req.DateFrom, req.DateTo)
	if err != nil {
		return nil, fmt.Errorf("failed to get dashboard overview: %w", err)
	}

	// Get top products (limit to 5 for dashboard)
	productReq := &models.ProductAnalyticsRequest{
		OrganizationID: req.OrganizationID,
		OutletID:       req.OutletID,
		DateFrom:       req.DateFrom,
		DateTo:         req.DateTo,
		Limit:          5,
	}
	topProducts, err := p.GetProductAnalytics(ctx, productReq)
	if err != nil {
		return nil, fmt.Errorf("failed to get top products: %w", err)
	}

	// Get payment methods
	paymentReq := &models.PaymentMethodAnalyticsRequest{
		OrganizationID: req.OrganizationID,
		OutletID:       req.OutletID,
		DateFrom:       req.DateFrom,
		DateTo:         req.DateTo,
		GroupBy:        "day",
	}
	paymentMethods, err := p.GetPaymentMethodAnalytics(ctx, paymentReq)
	if err != nil {
		return nil, fmt.Errorf("failed to get payment methods: %w", err)
	}

	// Get recent sales (last 7 days)
	recentDateFrom := time.Now().AddDate(0, 0, -7)
	salesReq := &models.SalesAnalyticsRequest{
		OrganizationID: req.OrganizationID,
		OutletID:       req.OutletID,
		DateFrom:       recentDateFrom,
		DateTo:         req.DateTo,
		GroupBy:        "day",
	}
	recentSales, err := p.GetSalesAnalytics(ctx, salesReq)
	if err != nil {
		return nil, fmt.Errorf("failed to get recent sales: %w", err)
	}

	return &models.DashboardAnalyticsResponse{
		OrganizationID: req.OrganizationID,
		OutletID:       req.OutletID,
		DateFrom:       req.DateFrom,
		DateTo:         req.DateTo,
		Overview: models.DashboardOverview{
			TotalSales:        overview.TotalSales,
			TotalOrders:       overview.TotalOrders,
			AverageOrderValue: overview.AverageOrderValue,
			TotalCustomers:    overview.TotalCustomers,
			VoidedOrders:      overview.VoidedOrders,
			RefundedOrders:    overview.RefundedOrders,
		},
		TopProducts:    topProducts.Data,
		PaymentMethods: paymentMethods.Data,
		RecentSales:    recentSales.Data,
	}, nil
}

func (p *AnalyticsProcessorImpl) GetProfitLossAnalytics(ctx context.Context, req *models.ProfitLossAnalyticsRequest) (*models.ProfitLossAnalyticsResponse, error) {
	if req.DateFrom.After(req.DateTo) {
		return nil, fmt.Errorf("date_from cannot be after date_to")
	}

	// Get analytics data from repository
	result, err := p.analyticsRepo.GetProfitLossAnalytics(ctx, req.OrganizationID, req.OutletID, req.DateFrom, req.DateTo, req.GroupBy)
	if err != nil {
		return nil, fmt.Errorf("failed to get profit/loss analytics: %w", err)
	}

	// Transform entities to models
	data := make([]models.ProfitLossData, len(result.Data))
	for i, item := range result.Data {
		data[i] = models.ProfitLossData{
			Date:              item.Date,
			Revenue:           item.Revenue,
			Cost:              item.Cost,
			GrossProfit:       item.GrossProfit,
			GrossProfitMargin: item.GrossProfitMargin,
			Tax:               item.Tax,
			Discount:          item.Discount,
			NetProfit:         item.NetProfit,
			NetProfitMargin:   item.NetProfitMargin,
			Orders:            item.Orders,
		}
	}

	productData := make([]models.ProductProfitData, len(result.ProductData))
	for i, item := range result.ProductData {
		productData[i] = models.ProductProfitData{
			ProductID:         item.ProductID,
			ProductName:       item.ProductName,
			CategoryID:        item.CategoryID,
			CategoryName:      item.CategoryName,
			QuantitySold:      item.QuantitySold,
			Revenue:           item.Revenue,
			Cost:              item.Cost,
			GrossProfit:       item.GrossProfit,
			GrossProfitMargin: item.GrossProfitMargin,
			AveragePrice:      item.AveragePrice,
			AverageCost:       item.AverageCost,
			ProfitPerUnit:     item.ProfitPerUnit,
		}
	}

	return &models.ProfitLossAnalyticsResponse{
		OrganizationID: req.OrganizationID,
		OutletID:       req.OutletID,
		DateFrom:       req.DateFrom,
		DateTo:         req.DateTo,
		GroupBy:        req.GroupBy,
		Summary: models.ProfitLossSummary{
			TotalRevenue:       result.Summary.TotalRevenue,
			TotalCost:          result.Summary.TotalCost,
			GrossProfit:        result.Summary.GrossProfit,
			GrossProfitMargin:  result.Summary.GrossProfitMargin,
			TotalTax:           result.Summary.TotalTax,
			TotalDiscount:      result.Summary.TotalDiscount,
			NetProfit:          result.Summary.NetProfit,
			NetProfitMargin:    result.Summary.NetProfitMargin,
			TotalOrders:        result.Summary.TotalOrders,
			AverageProfit:      result.Summary.AverageProfit,
			ProfitabilityRatio: result.Summary.ProfitabilityRatio,
		},
		Data:        data,
		ProductData: productData,
	}, nil
}
