package account

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"gitea.pena/PenaSide/common/log_mw"
	"gitea.pena/SQuiz/common/dal"
	"gitea.pena/SQuiz/common/middleware"
	"gitea.pena/SQuiz/common/model"
	"gitea.pena/SQuiz/common/pj_errors"
	"gitea.pena/SQuiz/core/internal/brokers"
	"gitea.pena/SQuiz/core/internal/clients/auth"
	"gitea.pena/SQuiz/core/internal/models"
	"github.com/go-redis/redis/v8"
	"github.com/gofiber/fiber/v2"
	"strconv"
	"time"
)

type Deps struct {
	Dal         *dal.DAL
	AuthClient  auth.IAuthClient
	Producer    brokers.IProducer
	ServiceName string
	RedisClient *redis.Client
}

type Account struct {
	dal         *dal.DAL
	authClient  auth.IAuthClient
	producer    brokers.IProducer
	serviceName string
	redisClient *redis.Client
}

func NewAccountController(deps Deps) *Account {
	return &Account{
		dal:         deps.Dal,
		authClient:  deps.AuthClient,
		producer:    deps.Producer,
		serviceName: deps.ServiceName,
		redisClient: deps.RedisClient,
	}
}

type CreateAccountReq struct {
	UserID string `json:"userId"`
}

type CreateAccountResp struct {
	CreatedAccount model.Account `json:"created_account"`
}

type DeleteAccountResp struct {
	DeletedAccountID string `json:"account_Id"`
}

type GetPrivilegeByUserIDReq struct {
	UserID string `json:"userId"`
}

type DeleteAccountByUserIDReq struct {
	UserID string `json:"userId"`
}

type DeleteAccountByUserIDResp struct {
	DeletedAccountUserID string `json:"userId"`
}

type GetAccountsReq struct {
	Limit uint64 `json:"limit"`
	Page  uint64 `json:"page"`
}

type GetAccountsResp struct {
	Count uint64          `json:"count"`
	Items []model.Account `json:"items"`
}

// getCurrentAccount обработчик для получения текущего аккаунта
func (r *Account) GetCurrentAccount(ctx *fiber.Ctx) error {
	accountID, ok := middleware.GetAccountId(ctx)
	if !ok {
		return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
	}

	account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
	if err != nil && err != sql.ErrNoRows {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	//TODO: fix this later
	if account.ID == "" {
		return ctx.Status(fiber.StatusNotFound).SendString("no account")
	}

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

// createAccount обработчик для создания нового аккаунта
func (r *Account) CreateAccount(ctx *fiber.Ctx) error {
	accountID, ok := middleware.GetAccountId(ctx)
	if !ok {
		return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
	}
	hlogger := log_mw.ExtractLogger(ctx)

	existingAccount, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
	if err != nil && err != sql.ErrNoRows {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}
	if existingAccount.ID != "" {
		return ctx.Status(fiber.StatusConflict).SendString("user with this ID already exists")
	}

	email, err := r.authClient.GetUserEmail(accountID)
	if err != nil {
		return err
	}

	newAccount := model.Account{
		UserID:    accountID,
		CreatedAt: time.Now(),
		Deleted:   false,
		Privileges: map[string]model.ShortPrivilege{
			"quizUnlimTime": {
				PrivilegeID:   "quizUnlimTime",
				PrivilegeName: "Безлимит Опросов",
				Amount:        14,
				CreatedAt:     time.Now(),
			},
		},
	}

	createdAcc, err := r.dal.AccountRepo.CreateAccount(ctx.Context(), &newAccount)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}
	_, err = r.dal.AccountRepo.PostLeadTarget(ctx.Context(), model.LeadTarget{
		AccountID: accountID,
		Target:    email,
		Type:      model.LeadTargetEmail,
		QuizID:    0,
	})
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	hlogger.Emit(models.InfoAccountCreated{
		CtxUserID:    accountID,
		CtxAccountID: createdAcc.ID,
	})

	err = r.producer.ToMailNotify(ctx.Context(), brokers.Message{
		AccountID:  accountID,
		Email:      email,
		ServiceKey: r.serviceName,
		SendAt:     time.Now(),
	})
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	return ctx.JSON(CreateAccountResp{
		CreatedAccount: newAccount,
	})
}

// deleteAccount обработчик для удаления текущего аккаунта
func (r *Account) DeleteAccount(ctx *fiber.Ctx) error {
	accountID, ok := middleware.GetAccountId(ctx)
	if !ok {
		return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
	}

	account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	if err := r.dal.AccountRepo.DeleteAccount(ctx.Context(), account.ID); err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	return ctx.JSON(DeleteAccountResp{
		DeletedAccountID: accountID,
	})
}

// getPrivilegeByUserID обработчик для получения привилегий аккаунта по ID пользователя
func (r *Account) GetPrivilegeByUserID(ctx *fiber.Ctx) error {
	var req GetPrivilegeByUserIDReq
	if err := ctx.BodyParser(&req); err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
	}

	privilege, err := r.dal.AccountRepo.GetPrivilegesByAccountID(ctx.Context(), req.UserID)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

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

// deleteAccountByUserID обработчик для удаления аккаунта по ID пользователя
func (r *Account) DeleteAccountByUserID(ctx *fiber.Ctx) error {
	var req DeleteAccountByUserIDReq
	if err := ctx.BodyParser(&req); err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
	}

	existingAccount, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), req.UserID)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	if existingAccount.ID == "" {
		return ctx.Status(fiber.StatusInternalServerError).SendString("user with this ID not found")
	}

	if err := r.dal.AccountRepo.DeleteAccount(ctx.Context(), existingAccount.ID); err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	return ctx.JSON(DeleteAccountByUserIDResp{
		DeletedAccountUserID: req.UserID,
	})
}

// getAccounts обработчик для получения списка аккаунтов с пагинацией
func (r *Account) GetAccounts(ctx *fiber.Ctx) error {
	var req GetAccountsReq
	if err := ctx.BodyParser(&req); err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
	}

	_, ok := middleware.GetAccountId(ctx)
	if !ok {
		return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
	}

	accounts, totalCount, err := r.dal.AccountRepo.GetAccounts(ctx.Context(), req.Limit, req.Page)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	response := GetAccountsResp{
		Count: totalCount,
		Items: accounts,
	}

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

func (r *Account) ManualDone(ctx *fiber.Ctx) error {
	var req struct {
		Id string `json:"id"`
	}
	if err := ctx.BodyParser(&req); err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
	}

	if req.Id == "" {
		return ctx.Status(fiber.StatusBadRequest).SendString("User id is required")
	}

	err := r.dal.AccountRepo.ManualDone(ctx.Context(), req.Id)
	if err != nil {
		if errors.Is(err, pj_errors.ErrNotFound) {
			return ctx.Status(fiber.StatusNotFound).SendString("user don't have this privilege")
		}
		return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
	}

	return ctx.SendStatus(fiber.StatusOK)
}

func (r *Account) PostLeadTarget(ctx *fiber.Ctx) error {
	var req struct {
		Type   string `json:"type"`
		QuizID int32  `json:"quizID"`
		Target string `json:"target"`
		Name   string `json:"name"`
	}
	if err := ctx.BodyParser(&req); err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
	}

	accountID, ok := middleware.GetAccountId(ctx)
	if !ok {
		return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
	}

	//accountID := "64f2cd7a7047f28fdabf6d9e"

	if _, ok := model.ValidLeadTargetTypes[req.Type]; !ok {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid type")
	}

	if req.Type == "" || (req.Target == "" && req.Type != string(model.LeadTargetTg)) {
		return ctx.Status(fiber.StatusBadRequest).SendString("Type and Target don't be nil")
	}

	switch req.Type {
	case "mail":
		_, err := r.dal.AccountRepo.PostLeadTarget(ctx.Context(), model.LeadTarget{
			AccountID: accountID,
			Target:    req.Target,
			Type:      model.LeadTargetType(req.Type),
			QuizID:    req.QuizID,
		})
		if err != nil {
			return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
		}
		return ctx.SendStatus(fiber.StatusOK)
	case "telegram":
		targets, err := r.dal.AccountRepo.GetLeadTarget(ctx.Context(), accountID, req.QuizID)
		if err != nil && !errors.Is(err, pj_errors.ErrNotFound) {
			return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
		}
		if !errors.Is(err, pj_errors.ErrNotFound) {
			for _, t := range targets {
				if t.Type == model.LeadTargetTg {
					return ctx.Status(fiber.StatusAlreadyReported).SendString("LeadTarget for this quiz already exist")
				}
			}
		}

		task := model.TgRedisTask{
			Name:      req.Name,
			QuizID:    req.QuizID,
			AccountID: accountID,
		}

		taskKey := fmt.Sprintf("telegram_task:%d", time.Now().UnixNano())
		taskData, err := json.Marshal(task)
		if err != nil {
			return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
		}

		if err := r.redisClient.Set(ctx.Context(), taskKey, taskData, 0).Err(); err != nil {
			return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
		}
	case "whatsapp":
		return ctx.Status(fiber.StatusOK).SendString("todo")
	}

	return nil
}

func (r *Account) DeleteLeadTarget(ctx *fiber.Ctx) error {
	leadIDStr := ctx.Params("id")
	leadID, err := strconv.ParseInt(leadIDStr, 10, 64)
	if err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid lead ID format")
	}

	err = r.dal.AccountRepo.DeleteLeadTarget(ctx.Context(), leadID)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}
	return ctx.SendStatus(fiber.StatusOK)
}

func (r *Account) GetLeadTarget(ctx *fiber.Ctx) error {
	accountID, ok := middleware.GetAccountId(ctx)
	if !ok {
		return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
	}

	quizIDStr := ctx.Params("quizID")
	quizID, err := strconv.ParseInt(quizIDStr, 10, 64)
	if err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid quiz ID format")
	}

	result, err := r.dal.AccountRepo.GetLeadTarget(ctx.Context(), accountID, int32(quizID))
	if err != nil {
		switch {
		case errors.Is(err, pj_errors.ErrNotFound):
			return ctx.Status(fiber.StatusNotFound).SendString("this lead target not found")
		default:
			return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
		}
	}

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

func (r *Account) UpdateLeadTarget(ctx *fiber.Ctx) error {
	var req struct {
		ID     int64  `json:"id"`
		Target string `json:"target"`
	}

	if err := ctx.BodyParser(&req); err != nil {
		return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
	}

	if req.ID == 0 || req.Target == "" {
		return ctx.Status(fiber.StatusBadRequest).SendString("ID and Target don't be nil")
	}

	result, err := r.dal.AccountRepo.UpdateLeadTarget(ctx.Context(), model.LeadTarget{
		ID:     req.ID,
		Target: req.Target,
	})
	if err != nil {
		switch {
		case errors.Is(err, pj_errors.ErrNotFound):
			return ctx.Status(fiber.StatusNotFound).SendString("this lead target not found")
		default:
			return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
		}
	}

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