package history_client

import (
	"fmt"
	"github.com/gofiber/fiber/v2"
	"go.uber.org/zap"
	"math"
	"os"
	"penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/client"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/history"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
	"strconv"
	"strings"
	"time"
)

type Deps struct {
	MiddleWare     *http.MiddleWare
	HistoryRepo    *repository.HistoryRepository
	AccountRepo    *repository.AccountRepository
	VerifyClient   *client.VerificationClient
	AuthClient     *client.AuthClient
	TemplateClient *client.TemplateClient
	CodewordClient *client.CodewordClient
	Logger         *zap.Logger
}

type HistoryController struct {
	middleWare     *http.MiddleWare
	historyRepo    *repository.HistoryRepository
	accountRepo    *repository.AccountRepository
	verifyClient   *client.VerificationClient
	authClient     *client.AuthClient
	templateClient *client.TemplateClient
	codewordClient *client.CodewordClient
	logger         *zap.Logger
}

func NewHistoryController(deps Deps) *HistoryController {
	return &HistoryController{
		middleWare:     deps.MiddleWare,
		historyRepo:    deps.HistoryRepo,
		authClient:     deps.AuthClient,
		accountRepo:    deps.AccountRepo,
		verifyClient:   deps.VerifyClient,
		templateClient: deps.TemplateClient,
		codewordClient: deps.CodewordClient,
		logger:         deps.Logger,
	}
}

func (receiver *HistoryController) Get(ctx *fiber.Ctx) error {
	var userID string

	pageStr := ctx.Query("page")
	limitStr := ctx.Query("limit")
	tipe := ctx.Query("type")
	accountID := ctx.Query("accountID")

	if accountID != "" {
		userID = accountID
	} else {
		id, ok := receiver.middleWare.ExtractUserID(ctx)
		if !ok || id == "" {
			return receiver.middleWare.NoAuth(ctx)
		}

		userID = id
	}

	limit, err := strconv.ParseInt(limitStr, 10, 64)
	if err != nil {
		return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "invalid limit format")
	}

	page, err := strconv.ParseInt(pageStr, 10, 64)
	if err != nil {
		return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "invalid page format")
	}

	dto := &history.GetHistories{
		UserID: userID,
		Type:   &tipe,
		Pagination: &models.Pagination{
			Page:  page,
			Limit: limit,
		},
	}

	count, err := receiver.historyRepo.CountAll(ctx.Context(), dto)
	if err != nil {
		return receiver.middleWare.ErrorOld(ctx, err)
	}

	if count == 0 {
		returnHistories := models.PaginationResponse[models.History]{TotalPages: 0, Records: []models.History{}}
		return ctx.Status(fiber.StatusOK).JSON(returnHistories)
	}

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

	histories, err := receiver.historyRepo.FindMany(ctx.Context(), dto)
	if err != nil {
		return receiver.middleWare.ErrorOld(ctx, err)
	}

	returnHistories := models.PaginationResponse[models.History]{
		TotalPages: totalPages,
		Records:    histories,
	}

	return ctx.Status(fiber.StatusOK).JSON(returnHistories)
}

func (receiver *HistoryController) GetRecentTariffs(ctx *fiber.Ctx) error {
	userID, ok := receiver.middleWare.ExtractUserID(ctx)
	if !ok || userID == "" {
		return receiver.middleWare.NoAuth(ctx)
	}

	tariffs, err := receiver.historyRepo.GetRecentTariffs(ctx.Context(), userID)
	if err != nil {
		receiver.logger.Error("failed to get recent tariffs on <GetRecentTariffs> of <API2>",
			zap.String("userId", userID),
			zap.Error(err),
		)
		return receiver.middleWare.ErrorOld(ctx, err)
	}

	return ctx.Status(fiber.StatusOK).JSON(tariffs)
}

func (receiver *HistoryController) SendReport(ctx *fiber.Ctx) error {
	hlogger := log_mw.ExtractLogger(ctx)

	var req struct {
		Id string `json:"id"`
	}
	if err := ctx.BodyParser(&req); err != nil {
		receiver.logger.Error("failed to bind request", zap.Error(err))
		return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "failed to bind request")
	}

	if req.Id == "" {
		receiver.logger.Error("history id is missing in <GetHistoryById> of <HistoryService>")
		return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "history id is missing")
	}

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

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

	historyMap, err := receiver.historyRepo.GetDocNumber(ctx.Context(), tariffs.UserID)
	if err != nil {
		receiver.logger.Error(
			"failed to get history of sorting by date created in <GetDocNumber> of <HistoryService>",
			zap.String("historyID", req.Id),
			zap.Error(err),
		)
		return receiver.middleWare.ErrorOld(ctx, err)
	}
	token := ctx.Get("Authorization")
	fmt.Println("HEADERS", ctx.Request().Header)

	verifuser, err := receiver.verifyClient.GetVerification(ctx.Context(), token, 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 receiver.middleWare.ErrorOld(ctx, err)
	}
	if !verifuser.Accepted {
		receiver.logger.Error(
			"verification not accepted",
			zap.String("userID", tariffs.UserID),
			zap.Error(err),
		)
		return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "verification not accepted")
	}
	account, err := receiver.accountRepo.FindByUserID(ctx.Context(), tariffs.UserID)
	if err != nil {
		return receiver.middleWare.ErrorOld(ctx, err)
	}

	authuser, err := receiver.authClient.GetUser(ctx.Context(), 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 receiver.middleWare.ErrorOld(ctx, err)
	}

	fileContents, readerr := os.ReadFile("./report.docx")
	if readerr != nil {
		return receiver.middleWare.Error(ctx, fiber.StatusInternalServerError, "failed to read file")
	}

	if account.Name.Orgname == "" {
		account.Name.Orgname = "Безымянное предприятие"
	}

	for _, tariff := range tariffs.RawDetails.Tariffs {
		totalAmount := uint64(0)
		privilegeMeasurement := ""
		piecePrice := ""
		privilegeName := ""
		for _, privilege := range tariff.Privileges {
			totalAmount += privilege.Amount
			privilegeMeasurement = string(privilege.Type)
			piecePrice = fmt.Sprintf("%.2f", float64(privilege.Price)/100)
			privilegeName = privilege.Name
		}
		data := models.RespGeneratorService{
			DocNumber: fmt.Sprint(historyMap[req.Id] + 1),
			Date:      time.Now().Format("2006-01-02"),
			OrgTaxNum: verifuser.TaxNumber,
			OrgName:   account.Name.Orgname,
			Name:      tariff.Name + " " + privilegeName,
			Amount:    fmt.Sprint(totalAmount),
			Unit:      piecePrice,
			Price:     fmt.Sprintf("%.2f", float64(tariffs.RawDetails.Price)/100),
			Sum:       privilegeMeasurement,
		}
		err = receiver.templateClient.SendData(ctx.Context(), data, fileContents, authuser.Login)
		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 receiver.middleWare.ErrorOld(ctx, err)
		}
	}

	hlogger.Emit(models.InfoReportRequest{
		CtxUserID:    tariffs.UserID,
		CtxAccountID: account.ID,
		CtxID:        req.Id,
		CtxTariff:    strings.Join(utils.GetTariffsIDs(tariffs.RawDetails.Tariffs), ","),
		CtxOrgName:   account.Name.Orgname,
	})

	return ctx.SendStatus(fiber.StatusOK)
}
