package service

import (
	"apskel-pos-be/internal/repository"
	"context"
	"fmt"
	"path/filepath"
	"strings"
	"time"

	"apskel-pos-be/internal/models"
	"apskel-pos-be/internal/processor"

	"github.com/google/uuid"
)

type ReportService interface {
	// Returns (publicURL, fileName, error)
	GenerateDailyTransactionPDF(ctx context.Context, organizationID string, outletID string, reportDate *time.Time, generatedBy string) (string, string, error)
}

type ReportServiceImpl struct {
	analyticsService AnalyticsService
	organizationRepo *repository.OrganizationRepositoryImpl
	outletRepo       *repository.OutletRepositoryImpl
	fileClient       processor.FileClient
}

func NewReportService(analyticsService *AnalyticsServiceImpl, organizationRepo *repository.OrganizationRepositoryImpl, outletRepo *repository.OutletRepositoryImpl, fileClient processor.FileClient) *ReportServiceImpl {
	return &ReportServiceImpl{
		analyticsService: analyticsService,
		organizationRepo: organizationRepo,
		outletRepo:       outletRepo,
		fileClient:       fileClient,
	}
}

// reportTemplateData holds the data passed to the HTML template
type reportTemplateData struct {
	OrganizationName string
	OutletName       string
	ReportDate       string
	StartDate        string
	EndDate          string
	GeneratedBy      string
	PrintTime        string
	Summary          reportSummary
	Items            []reportItem
}

type reportSummary struct {
	TotalTransactions  int64
	TotalItems         int64
	GrossSales         string
	Discount           string
	Tax                string
	NetSales           string
	COGS               string
	GrossProfit        string
	GrossMarginPercent string
}

type reportItem struct {
	Name        string
	Quantity    int64
	GrossSales  string
	Discount    string
	NetSales    string
	COGS        string
	GrossProfit string
}

func (s *ReportServiceImpl) GenerateDailyTransactionPDF(ctx context.Context, organizationID string, outletID string, reportDate *time.Time, generatedBy string) (string, string, error) {
	// Parse IDs
	orgID, err := uuid.Parse(organizationID)
	if err != nil {
		return "", "", fmt.Errorf("invalid organization id: %w", err)
	}
	outID, err := uuid.Parse(outletID)
	if err != nil {
		return "", "", fmt.Errorf("invalid outlet id: %w", err)
	}

	org, err := s.organizationRepo.GetByID(ctx, orgID)
	if err != nil {
		return "", "", fmt.Errorf("organization not found: %w", err)
	}

	outlet, err := s.outletRepo.GetByID(ctx, outID)
	if err != nil {
		return "", "", fmt.Errorf("outlet not found: %w", err)
	}

	tzName := "Asia/Jakarta"
	if outlet.Timezone != nil && *outlet.Timezone != "" {
		tzName = *outlet.Timezone
	}

	loc, locErr := time.LoadLocation(tzName)
	if locErr != nil || loc == nil {
		loc = time.Local
	}

	var day time.Time
	if reportDate != nil {
		t := reportDate.UTC()
		day = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc)
	} else {
		now := time.Now().In(loc)
		day = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
	}
	start := day
	end := day.Add(24*time.Hour - time.Nanosecond)

	salesReq := &models.SalesAnalyticsRequest{OrganizationID: orgID, OutletID: &outID, DateFrom: start, DateTo: end, GroupBy: "day"}
	plReq := &models.ProfitLossAnalyticsRequest{OrganizationID: orgID, OutletID: &outID, DateFrom: start, DateTo: end, GroupBy: "day"}

	sales, err := s.analyticsService.GetSalesAnalytics(ctx, salesReq)
	if err != nil {
		return "", "", fmt.Errorf("get sales analytics: %w", err)
	}
	pl, err := s.analyticsService.GetProfitLossAnalytics(ctx, plReq)
	if err != nil {
		return "", "", fmt.Errorf("get profit/loss analytics: %w", err)
	}

	data := reportTemplateData{
		OrganizationName: org.Name,
		OutletName:       outlet.Name,
		ReportDate:       day.Format("02/01/2006"),
		StartDate:        start.Format("02/01/2006 15:04"),
		EndDate:          end.Format("02/01/2006 15:04"),
		GeneratedBy:      generatedBy,
		PrintTime:        time.Now().Format("02/01/2006 15:04:05"),
		Summary: reportSummary{
			TotalTransactions:  pl.Summary.TotalOrders,
			TotalItems:         sales.Summary.TotalItems,
			GrossSales:         formatCurrency(pl.Summary.TotalRevenue),
			Discount:           formatCurrency(pl.Summary.TotalDiscount),
			Tax:                formatCurrency(pl.Summary.TotalTax),
			NetSales:           formatCurrency(sales.Summary.NetSales),
			COGS:               formatCurrency(pl.Summary.TotalCost),
			GrossProfit:        formatCurrency(pl.Summary.GrossProfit),
			GrossMarginPercent: fmt.Sprintf("%.2f", pl.Summary.GrossProfitMargin),
		},
	}

	items := make([]reportItem, 0, len(pl.ProductData))
	for _, p := range pl.ProductData {
		items = append(items, reportItem{
			Name:        p.ProductName,
			Quantity:    p.QuantitySold,
			GrossSales:  formatCurrency(p.Revenue),
			Discount:    formatCurrency(0),
			NetSales:    formatCurrency(p.Revenue),
			COGS:        formatCurrency(p.Cost),
			GrossProfit: formatCurrency(p.GrossProfit),
		})
	}
	data.Items = items

	templatePath := filepath.Join("templates", "daily_transaction.html")
	pdfBytes, err := renderTemplateToPDF(templatePath, data)
	if err != nil {
		return "", "", fmt.Errorf("render pdf: %w", err)
	}

	// Upload to bucket
	safeOutlet := outID.String()
	safeOrg := orgID.String()

	// Clean outlet name for filename (remove spaces and special characters)
	cleanOutletName := strings.ReplaceAll(outlet.Name, " ", "-")
	cleanOutletName = strings.ReplaceAll(cleanOutletName, "/", "-")
	cleanOutletName = strings.ReplaceAll(cleanOutletName, "\\", "-")
	cleanOutletName = strings.ReplaceAll(cleanOutletName, ":", "-")
	cleanOutletName = strings.ReplaceAll(cleanOutletName, "*", "-")
	cleanOutletName = strings.ReplaceAll(cleanOutletName, "?", "-")
	cleanOutletName = strings.ReplaceAll(cleanOutletName, "\"", "-")
	cleanOutletName = strings.ReplaceAll(cleanOutletName, "<", "-")
	cleanOutletName = strings.ReplaceAll(cleanOutletName, ">", "-")
	cleanOutletName = strings.ReplaceAll(cleanOutletName, "|", "-")

	fileName := fmt.Sprintf("laporan-transaksi-harian-%s-%s-%s.pdf", cleanOutletName, day.Format("2006-01-02"), time.Now().Format("20060102-150405"))
	objectKey := fmt.Sprintf("/reports/%s/%s/%s", safeOrg, safeOutlet, fileName)
	publicURL, err := s.fileClient.UploadFile(ctx, objectKey, pdfBytes)
	if err != nil {
		return "", "", fmt.Errorf("upload pdf: %w", err)
	}

	return publicURL, fileName, nil
}
