package history

import (
	"context"
	"fmt"
	"log"
	"math"
	"os"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.uber.org/zap"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/fields"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
)

type GetHistories struct {
	Pagination *models.Pagination
	Type       *string
	UserID     string
}

func (receiver *GetHistories) BSON() bson.M {
	query := bson.M{
		fields.History.IsDeleted: false,
		fields.History.Type:      *receiver.Type,
		fields.History.UserID: 	  receiver.UserID,
	}

	return query
}

type historyRepository interface {
	CountAll(context.Context, *GetHistories) (int64, errors.Error)
	FindMany(context.Context, *GetHistories) ([]models.History, errors.Error)
	Insert(context.Context, *models.History) (*models.History, errors.Error)
	GetRecentTariffs(context.Context, string) ([]models.TariffID, errors.Error) // new
	GetHistoryByID(context.Context, string) (*models.ReportHistory, errors.Error)
	GetDocNumber(context.Context, string) (map[string]int, errors.Error)
	CalculateCustomerLTV(ctx context.Context, from, to int64) (int64, errors.Error)
}

type authClient interface {
	GetUser(ctx context.Context, userID string) (*models.User, errors.Error)
}

type verificationClient interface {
	GetUser(ctx context.Context, userID string) (*models.Verification, errors.Error)
}

type temlategenClient interface {
	SendData(ctx context.Context, data models.RespGeneratorService, fileContents []byte, email string) errors.Error
}

type Deps struct {
	Logger             *zap.Logger
	Repository         historyRepository
	AuthClient         authClient
	VerificationClient verificationClient
	TemlategenClient   temlategenClient
}

type Service struct {
	logger             *zap.Logger
	repository         historyRepository
	AuthClient         authClient
	VerificationClient verificationClient
	TemlategenClient   temlategenClient
}

func New(deps Deps) *Service {
	if deps.Logger == nil {
		log.Panicln("logger is nil on <New (history service)>")
	}

	if deps.Repository == nil {
		log.Panicln("repository is nil on <New (history service)>")
	}

	if deps.AuthClient == nil {
		log.Panicln("auth client is nil on <New (account service)>")
	}

	return &Service{
		logger:     deps.Logger,
		repository: deps.Repository,
		AuthClient: deps.AuthClient,
	}
}

func (receiver *Service) GetHistoryList(ctx context.Context, dto *GetHistories) (*models.PaginationResponse[models.History], errors.Error) {
	if dto == nil {
		return nil, errors.New(
			fmt.Errorf("pagination is nil on <GetHistoryList> of <HistoryService>: %w", errors.ErrInternalError),
			errors.ErrInternalError,
		)
	}

	count, err := receiver.repository.CountAll(ctx, dto)
	if err != nil {
		receiver.logger.Error("failed to count histories on <GetHistoryList> of <HistoryService>",
			zap.Error(err),
		)

		return nil, err
	}

	if count == 0 {
		return &models.PaginationResponse[models.History]{TotalPages: 0, Records: []models.History{}}, nil
	}

	totalPages := int64(math.Ceil(float64(count) / float64(dto.Pagination.Limit)))

	histories, err := receiver.repository.FindMany(ctx, dto)
	if err != nil {
		receiver.logger.Error("failed to get historiy list on <GetHistoryList> of <HistoryService>",
			zap.Error(err),
		)

		return nil, err
	}

	return &models.PaginationResponse[models.History]{
		TotalPages: totalPages,
		Records:    histories,
	}, nil
}

func (receiver *Service) CreateHistory(ctx context.Context, history *models.History) (*models.History, errors.Error) {
	createdHistory, err := receiver.repository.Insert(ctx, history)
	if err != nil {
		receiver.logger.Error("failed to create history on <CreateHistory> of <HistoryService>", zap.Error(err))
		return nil, err
	}

	return createdHistory, nil
}

// TODO:tests.
func (receiver *Service) GetRecentTariffs(ctx context.Context, userID string) ([]models.TariffID, errors.Error) {
	if userID == "" {
		receiver.logger.Error("user id is missing in <GetRecentTariffs> of <HistoryService>")
		return nil, errors.New(
			fmt.Errorf("user id is missing: %w", errors.ErrInvalidArgs),
			errors.ErrInvalidArgs,
		)
	}

	tariffs, err := receiver.repository.GetRecentTariffs(ctx, userID)
	if err != nil {
		receiver.logger.Error(
			"failed to get recent tariffs in <GetRecentTariffs> of <HistoryService>",
			zap.String("userId", userID),
			zap.Error(err),
		)
		return nil, err
	}

	return tariffs, nil
}

func (receiver *Service) GetHistoryByID(ctx context.Context, historyID string) errors.Error {
	if historyID == "" {
		receiver.logger.Error("history id is missing in <GetHistoryById> of <HistoryService>")
		return errors.New(
			fmt.Errorf("history id is missing: %w", errors.ErrInvalidArgs),
			errors.ErrInvalidArgs,
		)
	}

	tariffs, err := receiver.repository.GetHistoryByID(ctx, historyID)
	if err != nil {
		receiver.logger.Error(
			"failed to get history by id in <GetHistoryById> of <HistoryService>",
			zap.String("historyID", historyID),
			zap.Error(err),
		)
		return err
	}

	if tariffs.Key != models.CustomerHistoryKeyPayCart {
		receiver.logger.Error(
			"invalid history record key",
			zap.String("historyID", historyID),
			zap.Error(err),
		)
		return err
	}

	historyMap, err := receiver.repository.GetDocNumber(ctx, tariffs.UserID)
	if err != nil {
		receiver.logger.Error(
			"failed to get history of sorting by date created in <GetDocNumber> of <HistoryService>",
			zap.String("historyID", historyID),
			zap.Error(err),
		)
		return err
	}

	verifuser, err := receiver.VerificationClient.GetUser(ctx, tariffs.UserID)
	if err != nil {
		receiver.logger.Error("failed to get user verification on <GetHistoryById> of <HistoryService>",
			zap.Error(err),
			zap.String("userID", tariffs.UserID),
		)

		return err
	}
	if !verifuser.Accepted {
		receiver.logger.Error(
			"verification not accepted",
			zap.String("userID", tariffs.UserID),
			zap.Error(err),
		)
		return err
	}

	authuser, err := receiver.AuthClient.GetUser(ctx, tariffs.UserID)
	if err != nil {
		receiver.logger.Error("failed to get user on <GetHistoryById> of <HistoryService>",
			zap.Error(err),
			zap.String("userID", tariffs.UserID),
		)

		return err
	}

	fileContents, readerr := os.ReadFile("./report.docx")
	if readerr != nil {
		return errors.New(
			fmt.Errorf("failed to read file: %w", errors.ErrInternalError),
			errors.ErrInternalError,
		)
	}

	for _, tariff := range tariffs.RawDetails.Tariffs {
		totalAmount := uint64(0)
		for _, privilege := range tariff.Privileges {
			totalAmount += privilege.Amount
		}
		data := models.RespGeneratorService{
			DocNumber: historyMap[historyID] + 1,
			Date:      time.Now().Format("2006-01-02"),
			OrgTaxNum: verifuser.TaxNumber,
			OrgName:   models.Name{Orgname: "Orgname"},
			Name:      tariff.Name,
			Amount:    totalAmount,
			Price:     tariffs.RawDetails.Price,
			Sum:       tariffs.RawDetails.Price,
		}
		err = receiver.TemlategenClient.SendData(ctx, data, fileContents, authuser.Email)
		if err != nil {
			receiver.logger.Error("failed to send report to user on <GetHistoryById> of <HistoryService>",
				zap.Error(err),
				zap.String("userID", tariffs.UserID),
			)

			return err
		}
	}
	return nil
}

func (receiver *Service) CalculateCustomerLTV(ctx context.Context, from, to int64) (int64, errors.Error) {
	ltv, err := receiver.repository.CalculateCustomerLTV(ctx, from, to)
	if err != nil {
		receiver.logger.Error("failed to calculate LTV", zap.Error(err))
		return 0, err
	}

	return ltv, nil
}
