package service

import (
	"context"
	"time"

	"apskel-pos-be/internal/appcontext"
	"apskel-pos-be/internal/constants"
	"apskel-pos-be/internal/contract"
	"apskel-pos-be/internal/models"
	"apskel-pos-be/internal/processor"
	"apskel-pos-be/internal/transformer"

	"github.com/google/uuid"
)

type InventoryService interface {
	CreateInventory(ctx context.Context, apctx *appcontext.ContextInfo, req *contract.CreateInventoryRequest) *contract.Response
	UpdateInventory(ctx context.Context, id uuid.UUID, req *contract.UpdateInventoryRequest) *contract.Response
	DeleteInventory(ctx context.Context, id uuid.UUID) *contract.Response
	GetInventoryByID(ctx context.Context, id uuid.UUID) *contract.Response
	ListInventory(ctx context.Context, req *contract.ListInventoryRequest) *contract.Response
	AdjustInventory(ctx context.Context, req *contract.AdjustInventoryRequest) *contract.Response
	RestockInventory(ctx context.Context, req *contract.RestockInventoryRequest) *contract.Response
	GetLowStockItems(ctx context.Context, outletID uuid.UUID) *contract.Response
	GetZeroStockItems(ctx context.Context, outletID uuid.UUID) *contract.Response
	GetInventoryReportSummary(ctx context.Context, outletID, organizationID uuid.UUID, dateFrom, dateTo *time.Time) (*contract.InventoryReportSummaryResponse, error)
	GetInventoryReportDetails(ctx context.Context, filter *models.InventoryReportFilter, organizationID uuid.UUID) (*contract.InventoryReportDetailResponse, error)
}

type InventoryServiceImpl struct {
	inventoryProcessor processor.InventoryProcessor
}

func NewInventoryService(inventoryProcessor processor.InventoryProcessor) *InventoryServiceImpl {
	return &InventoryServiceImpl{
		inventoryProcessor: inventoryProcessor,
	}
}

func (s *InventoryServiceImpl) CreateInventory(ctx context.Context, apctx *appcontext.ContextInfo, req *contract.CreateInventoryRequest) *contract.Response {
	modelReq := transformer.CreateInventoryRequestToModel(req)

	inventoryResponse, err := s.inventoryProcessor.Create(ctx, modelReq, apctx.OrganizationID)
	if err != nil {
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.InventoryServiceEntity, err.Error())
		return contract.BuildErrorResponse([]*contract.ResponseError{errorResp})
	}

	contractResponse := transformer.InventoryModelResponseToResponse(inventoryResponse)
	return contract.BuildSuccessResponse(contractResponse)
}

func (s *InventoryServiceImpl) UpdateInventory(ctx context.Context, id uuid.UUID, req *contract.UpdateInventoryRequest) *contract.Response {
	modelReq := transformer.UpdateInventoryRequestToModel(req)

	inventoryResponse, err := s.inventoryProcessor.Update(ctx, id, modelReq, uuid.Nil) // TODO: Get organizationID from context
	if err != nil {
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.InventoryServiceEntity, err.Error())
		return contract.BuildErrorResponse([]*contract.ResponseError{errorResp})
	}

	contractResponse := transformer.InventoryModelResponseToResponse(inventoryResponse)
	return contract.BuildSuccessResponse(contractResponse)
}

func (s *InventoryServiceImpl) DeleteInventory(ctx context.Context, id uuid.UUID) *contract.Response {
	err := s.inventoryProcessor.Delete(ctx, id, uuid.Nil) // TODO: Get organizationID from context
	if err != nil {
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.InventoryServiceEntity, err.Error())
		return contract.BuildErrorResponse([]*contract.ResponseError{errorResp})
	}

	return contract.BuildSuccessResponse(map[string]interface{}{
		"message": "Inventory deleted successfully",
	})
}

func (s *InventoryServiceImpl) GetInventoryByID(ctx context.Context, id uuid.UUID) *contract.Response {
	inventoryResponse, err := s.inventoryProcessor.GetByID(ctx, id, uuid.Nil) // TODO: Get organizationID from context
	if err != nil {
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.InventoryServiceEntity, err.Error())
		return contract.BuildErrorResponse([]*contract.ResponseError{errorResp})
	}

	contractResponse := transformer.InventoryModelResponseToResponse(inventoryResponse)
	return contract.BuildSuccessResponse(contractResponse)
}

func (s *InventoryServiceImpl) ListInventory(ctx context.Context, req *contract.ListInventoryRequest) *contract.Response {
	// Build filters
	filters := make(map[string]interface{})
	if req.OutletID != nil {
		filters["outlet_id"] = *req.OutletID
	}
	if req.ProductID != nil {
		filters["product_id"] = *req.ProductID
	}
	if req.CategoryID != nil {
		filters["category_id"] = *req.CategoryID
	}
	if req.LowStockOnly != nil && *req.LowStockOnly {
		filters["low_stock"] = true
	}
	if req.ZeroStockOnly != nil && *req.ZeroStockOnly {
		filters["zero_stock"] = true
	}
	if req.Search != "" {
		filters["search"] = req.Search
	}

	inventory, totalCount, err := s.inventoryProcessor.List(ctx, filters, req.Limit, req.Page, uuid.Nil) // TODO: Get organizationID from context
	if err != nil {
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.InventoryServiceEntity, err.Error())
		return contract.BuildErrorResponse([]*contract.ResponseError{errorResp})
	}

	// Convert from []*models.InventoryResponse to []models.InventoryResponse
	var inventoryResponses []models.InventoryResponse
	for _, inv := range inventory {
		inventoryResponses = append(inventoryResponses, *inv)
	}

	// Convert to contract responses
	contractResponses := transformer.InventoryToResponses(inventoryResponses)

	// Calculate total pages
	totalPages := int(totalCount) / req.Limit
	if int(totalCount)%req.Limit > 0 {
		totalPages++
	}

	listResponse := &contract.ListInventoryResponse{
		Inventory:  contractResponses,
		TotalCount: int(totalCount),
		Page:       req.Page,
		Limit:      req.Limit,
		TotalPages: totalPages,
	}

	return contract.BuildSuccessResponse(listResponse)
}

func (s *InventoryServiceImpl) AdjustInventory(ctx context.Context, req *contract.AdjustInventoryRequest) *contract.Response {
	modelReq := transformer.AdjustInventoryRequestToModel(req)

	inventoryResponse, err := s.inventoryProcessor.AdjustQuantity(ctx, modelReq.ProductID, modelReq.OutletID, uuid.Nil, modelReq.Delta) // TODO: Get organizationID from context
	if err != nil {
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.InventoryServiceEntity, err.Error())
		return contract.BuildErrorResponse([]*contract.ResponseError{errorResp})
	}

	contractResponse := transformer.InventoryModelResponseToResponse(inventoryResponse)
	return contract.BuildSuccessResponse(contractResponse)
}

func (s *InventoryServiceImpl) RestockInventory(ctx context.Context, req *contract.RestockInventoryRequest) *contract.Response {
	restockResponse, err := s.inventoryProcessor.RestockInventory(ctx, req.OutletID, req.Items, req.Reason)
	if err != nil {
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.InventoryServiceEntity, err.Error())
		return contract.BuildErrorResponse([]*contract.ResponseError{errorResp})
	}

	return contract.BuildSuccessResponse(restockResponse)
}

func (s *InventoryServiceImpl) GetLowStockItems(ctx context.Context, outletID uuid.UUID) *contract.Response {
	inventory, err := s.inventoryProcessor.GetLowStock(ctx, outletID, uuid.Nil) // TODO: Get organizationID from context
	if err != nil {
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.InventoryServiceEntity, err.Error())
		return contract.BuildErrorResponse([]*contract.ResponseError{errorResp})
	}

	// Convert from []*models.InventoryResponse to []models.InventoryResponse
	var inventoryResponses []models.InventoryResponse
	for _, inv := range inventory {
		inventoryResponses = append(inventoryResponses, *inv)
	}

	contractResponses := transformer.InventoryToResponses(inventoryResponses)
	return contract.BuildSuccessResponse(contractResponses)
}

func (s *InventoryServiceImpl) GetZeroStockItems(ctx context.Context, outletID uuid.UUID) *contract.Response {
	inventory, err := s.inventoryProcessor.GetZeroStock(ctx, outletID, uuid.Nil) // TODO: Get organizationID from context
	if err != nil {
		errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.InventoryServiceEntity, err.Error())
		return contract.BuildErrorResponse([]*contract.ResponseError{errorResp})
	}

	// Convert from []*models.InventoryResponse to []models.InventoryResponse
	var inventoryResponses []models.InventoryResponse
	for _, inv := range inventory {
		inventoryResponses = append(inventoryResponses, *inv)
	}

	contractResponses := transformer.InventoryToResponses(inventoryResponses)
	return contract.BuildSuccessResponse(contractResponses)
}

// GetInventoryReportSummary returns summary statistics for inventory report
func (s *InventoryServiceImpl) GetInventoryReportSummary(ctx context.Context, outletID, organizationID uuid.UUID, dateFrom, dateTo *time.Time) (*contract.InventoryReportSummaryResponse, error) {
	summary, err := s.inventoryProcessor.GetInventoryReportSummary(ctx, outletID, organizationID, dateFrom, dateTo)
	if err != nil {
		return nil, err
	}

	return &contract.InventoryReportSummaryResponse{
		TotalProducts:        summary.TotalProducts,
		TotalIngredients:     summary.TotalIngredients,
		TotalValue:           summary.TotalValue,
		LowStockProducts:     summary.LowStockProducts,
		LowStockIngredients:  summary.LowStockIngredients,
		ZeroStockProducts:    summary.ZeroStockProducts,
		ZeroStockIngredients: summary.ZeroStockIngredients,
		TotalSoldProducts:    summary.TotalSoldProducts,
		TotalSoldIngredients: summary.TotalSoldIngredients,
		OutletID:             summary.OutletID.String(),
		OutletName:           summary.OutletName,
		GeneratedAt:          summary.GeneratedAt.Format(time.RFC3339),
	}, nil
}

// GetInventoryReportDetails returns detailed inventory report with products and ingredients
func (s *InventoryServiceImpl) GetInventoryReportDetails(ctx context.Context, filter *models.InventoryReportFilter, organizationID uuid.UUID) (*contract.InventoryReportDetailResponse, error) {
	report, err := s.inventoryProcessor.GetInventoryReportDetails(ctx, filter, organizationID)
	if err != nil {
		return nil, err
	}

	response := &contract.InventoryReportDetailResponse{}

	if report.Summary != nil {
		response.Summary = &contract.InventoryReportSummaryResponse{
			TotalProducts:        report.Summary.TotalProducts,
			TotalIngredients:     report.Summary.TotalIngredients,
			TotalValue:           report.Summary.TotalValue,
			LowStockProducts:     report.Summary.LowStockProducts,
			LowStockIngredients:  report.Summary.LowStockIngredients,
			ZeroStockProducts:    report.Summary.ZeroStockProducts,
			ZeroStockIngredients: report.Summary.ZeroStockIngredients,
			TotalSoldProducts:    report.Summary.TotalSoldProducts,
			TotalSoldIngredients: report.Summary.TotalSoldIngredients,
			OutletID:             report.Summary.OutletID.String(),
			OutletName:           report.Summary.OutletName,
			GeneratedAt:          report.Summary.GeneratedAt.Format(time.RFC3339),
		}
	}

	response.Products = make([]*contract.InventoryProductDetailResponse, len(report.Products))
	for i, product := range report.Products {
		response.Products[i] = &contract.InventoryProductDetailResponse{
			ID:           product.ID.String(),
			ProductID:    product.ProductID.String(),
			ProductName:  product.ProductName,
			CategoryName: product.CategoryName,
			Quantity:     product.Quantity,
			ReorderLevel: product.ReorderLevel,
			UnitCost:     product.UnitCost,
			TotalValue:   product.TotalValue,
			TotalIn:      product.TotalIn,
			TotalOut:     product.TotalOut,
			IsLowStock:   product.IsLowStock,
			IsZeroStock:  product.IsZeroStock,
			UpdatedAt:    product.UpdatedAt.Format(time.RFC3339),
		}
	}

	// Transform ingredients
	response.Ingredients = make([]*contract.InventoryIngredientDetailResponse, len(report.Ingredients))
	for i, ingredient := range report.Ingredients {
		response.Ingredients[i] = &contract.InventoryIngredientDetailResponse{
			ID:             ingredient.ID.String(),
			IngredientID:   ingredient.IngredientID.String(),
			IngredientName: ingredient.IngredientName,
			UnitName:       ingredient.UnitName,
			Quantity:       ingredient.Quantity,
			ReorderLevel:   ingredient.ReorderLevel,
			UnitCost:       ingredient.UnitCost,
			TotalValue:     ingredient.TotalValue,
			TotalIn:        ingredient.TotalIn,
			TotalOut:       ingredient.TotalOut,
			IsLowStock:     ingredient.IsLowStock,
			IsZeroStock:    ingredient.IsZeroStock,
			UpdatedAt:      ingredient.UpdatedAt.Format(time.RFC3339),
		}
	}

	return response, nil
}
