package tickets

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"gitea.pena/PenaSide/heruvym/internal/model"
	"gitea.pena/PenaSide/heruvym/internal/repository/mongo"
	"gitea.pena/PenaSide/heruvym/internal/tools"
	"gitea.pena/PenaSide/heruvym/internal/utils/jwt_adapter"
	"gitea.pena/PenaSide/heruvym/internal/utils/middleware"
	"gitea.pena/PenaSide/hlog"
	"github.com/gofiber/fiber/v2"
	"go.uber.org/zap"
	"gopkg.in/telebot.v3"
)

type Deps struct {
	Dal       *mongo.DAL
	Notifier  *telebot.Bot
	TgChatID  int64
	HLogger   hlog.Logger
	ZapLogger *zap.Logger
}

type TicketController struct {
	dal       *mongo.DAL
	notifier  *telebot.Bot
	tgChatID  int64
	hLogger   hlog.Logger
	zapLogger *zap.Logger
}

func NewTicketController(deps Deps) *TicketController {
	return &TicketController{
		dal:       deps.Dal,
		notifier:  deps.Notifier,
		tgChatID:  deps.TgChatID,
		hLogger:   deps.HLogger,
		zapLogger: deps.ZapLogger,
	}
}

type CreateTicketReq struct {
	Title   string `json:"Title"`
	Message string `json:"Message"`
	System  bool   `json:"System"`
}

type CreateTicketResp struct {
	Ticket string `json:"Ticket"`
	Sess   string `json:"sess"`
}

func (t *TicketController) CreateTicket(ctx *fiber.Ctx) error {
	var (
		err     error
		request CreateTicketReq
	)

	err = ctx.BodyParser(&request)
	if err != nil {
		return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
	}

	if request.Title == "" {
		return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "title is required"})
	}

	if request.Message == "" {
		return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "message is required"})
	}

	session := jwt_adapter.Get(ctx.Context())
	if session == nil {
		return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"})
	}

	var (
		ticketID string
		tickets  []model.Ticket
		role     = jwt_adapter.GetRole(ctx.Context())
	)
	if role == "" {
		tickets, _, err = t.dal.GetTickets4Sess(ctx.Context(), session.Id)
	}

	if err != nil || len(tickets) == 0 {
		ticketID, err = t.dal.CreateTicket(
			ctx.Context(),
			session.Id,
			session.Id,
			ctx.Get("Origin"),
			request.Title,
			request.Message,
			request.System,
			[]string{},
		)
		if err != nil {
			return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
		}

		if _, err := t.dal.PutMessage(ctx.Context(),
			request.Message,
			session.Id,
			session.Id,
			ticketID,
			[]string{},
			request.System, // system error flag
		); err != nil {
			return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
		}

		domain, ok := ctx.Context().Value(middleware.HostKey).(string)
		if !ok || domain == "" {
			return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "domain is nil"})
		}

		go func() {
			if session.Id != "" && role != "admin" {
				if err == nil && t.notifier != nil {
					var userLink, supportLink string
					if session.StandardClaims.Issuer != "" {
						if len(domain) > 0 && domain[0] == 's' {
							userLink = fmt.Sprintf("https://sadmin.pena/users/%s", session.Id)
							supportLink = fmt.Sprintf("https://sadmin.pena/support/%s", ticketID)
						} else {
							userLink = fmt.Sprintf("https://admin.pena/users/%s", session.Id)
							supportLink = fmt.Sprintf("https://admin.pena/support/%s", ticketID)
						}
					} else {
						if len(domain) > 0 && domain[0] == 's' {
							supportLink = fmt.Sprintf("https://sadmin.pena/support/%s", ticketID)
						} else {
							supportLink = fmt.Sprintf("https://admin.pena/support/%s", ticketID)
						}
						userLink = "незарегистрированного пользователя"
					}

					message := fmt.Sprintf("Вам пришло сообщение от %s сссылка на пользователя с %s, ccылка на чат - %s",
						userLink, domain, supportLink)

					if _, err := t.notifier.Send(telebot.ChatID(t.tgChatID), message); err != nil {
						fmt.Println("CAN NOT NOTIFY", err)
					}
					return
				}
			}
		}()

	} else {
		ticketID = tickets[0].ID
	}

	return ctx.Status(fiber.StatusOK).JSON(CreateTicketResp{Ticket: ticketID, Sess: session.Id})
}

type ReqPutMessage struct {
	Message  string   `json:"message"`
	TicketID string   `json:"ticket"`
	Files    []string `json:"files"`
	Lang     string   `json:"lang"`
	System   bool     `json:"system"`
}

func (t *TicketController) PutMessage(ctx *fiber.Ctx) error {
	var request ReqPutMessage
	err := ctx.BodyParser(&request)
	if err != nil {
		return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
	}
	sess := jwt_adapter.Get(ctx.Context())
	request.Files = []string{}

	domain, ok := ctx.Context().Value(middleware.HostKey).(string)
	if !ok || domain == "" {
		return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "domain is nil"})
	}

	message, err := t.dal.PutMessage(
		ctx.Context(),
		request.Message,
		sess.Id,
		sess.Id,
		request.TicketID,
		[]string{},
		request.System,
	)
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	role := jwt_adapter.GetRole(ctx.Context())
	go func() {
		if sess.Id != "" && role != "admin" {
			if err == nil && t.notifier != nil {
				var userLink, supportLink string
				if sess.StandardClaims.Issuer != "" {
					if domain[0] == 's' {
						userLink = fmt.Sprintf("https://sadmin.pena/users/%s", sess.Id)
						supportLink = fmt.Sprintf("https://sadmin.pena/support/%s", request.TicketID)
					} else {
						userLink = fmt.Sprintf("https://admin.pena/users/%s", sess.Id)
						supportLink = fmt.Sprintf("https://admin.pena/support/%s", request.TicketID)
					}
				} else {
					if domain[0] == 's' {
						supportLink = fmt.Sprintf("https://sadmin.pena/support/%s", request.TicketID)
					} else {
						supportLink = fmt.Sprintf("https://admin.pena/support/%s", request.TicketID)
					}
					userLink = "незарегистрированного пользователя"
				}

				var message string
				if request.System {
					message = fmt.Sprintf("СИСТЕМНАЯ ОШИБКА! Произошла ошибка в сообщении от %s c %s. Ссылка на чат: %s", userLink, domain, supportLink)
				} else {
					message = fmt.Sprintf("Вам пришло сообщение от %s ссылка на пользователя с %s, ссылка на чат - %s",
						userLink, domain, supportLink)
				}

				if _, err := t.notifier.Send(telebot.ChatID(t.tgChatID), message); err != nil {
					t.zapLogger.Error("CAN NOT NOTIFY", zap.Error(err))
				}
				return
			}
		}
	}()

	if err := t.dal.UpdateTopMessage(ctx.Context(), request.TicketID, message); err != nil {
		fmt.Println("PUTMES01", err, request.TicketID, message)
		return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
		//return errors.New("can not update ticket"), http.StatusInternalServerError
	}

	return ctx.SendStatus(fiber.StatusOK)
}

type GetTicketsReq struct {
	Amount int64  `json:"amt"`
	Page   int64  `json:"page"`
	Search string `json:"srch"`
	Status string `json:"status"`
}
type GetTicketsResp struct {
	Data  []model.Ticket `json:"data"`
	Count int64          `json:"count"`
}

func (t *TicketController) GetTickets(ctx *fiber.Ctx) error {
	var request GetTicketsReq
	if err := ctx.BodyParser(&request); err != nil {
		return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
	}

	role := jwt_adapter.GetRole(ctx.Context())
	if role == "admin" {
		result, count, err := t.dal.GetTicketPage(ctx.Context(),
			request.Status,
			request.Search,
			request.Amount,
			request.Page,
		)
		if err != nil {
			return ctx.Status(fiber.StatusNoContent).JSON(fiber.Map{"error": "No Content"})
		}

		return ctx.Status(fiber.StatusOK).JSON(GetTicketsResp{
			Data:  *result,
			Count: count,
		})
	} else {
		sess := jwt_adapter.Get(ctx.Context())

		result, count, err := t.dal.YieldUserTickets(ctx.Context(), sess.Id, request.Amount, request.Page*request.Amount)
		if err != nil {
			return ctx.Status(fiber.StatusNoContent).JSON(fiber.Map{"error": "No Content"})
		}

		return ctx.Status(fiber.StatusOK).JSON(GetTicketsResp{
			Data:  result,
			Count: count,
		})
	}
}

type GetMessagesReq struct {
	Amount   int64  `json:"amt"`
	Page     int64  `json:"page"`
	Search   string `json:"srch"`
	TicketID string `json:"ticket"`
}

func (t *TicketController) GetMessages(ctx *fiber.Ctx) error {
	var request GetMessagesReq
	if err := ctx.BodyParser(&request); err != nil {
		return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
	}

	role := jwt_adapter.GetRole(ctx.Context())

	result, err := t.dal.GetMessagesPage(ctx.Context(),
		request.Search,
		request.TicketID,
		role,
		request.Amount,
		request.Page,
	)
	if err != nil {
		return ctx.Status(fiber.StatusNoContent).JSON(fiber.Map{"error": "No Content"})
	}

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

type PickReq struct {
	TicketID string `json:"ticket"`
}

func (t *TicketController) Pick(ctx *fiber.Ctx) error {
	var request PickReq
	if err := ctx.BodyParser(&request); err != nil {
		return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
	}
	sess := jwt_adapter.Get(ctx.Context())
	if err := t.dal.SetAnswerer(ctx.Context(), request.TicketID, sess.Id); err != nil {
		return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	return ctx.SendStatus(fiber.StatusOK)
}

type DelegateReq struct {
	TicketID   string `json:"ticket"`
	AnswererID string `json:"answerer"`
}

func (t *TicketController) Delegate(ctx *fiber.Ctx) error {
	var request DelegateReq
	if err := ctx.BodyParser(&request); err != nil {
		return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
	}
	if err := t.dal.SetAnswerer(ctx.Context(), request.TicketID, request.AnswererID); err != nil {
		return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	return ctx.SendStatus(fiber.StatusOK)
}

type VoteReq struct {
	TicketID string `json:"ticket"`
	Rate     int    `json:"rate"`
}

func (t *TicketController) Vote(ctx *fiber.Ctx) error {
	var request VoteReq
	if err := ctx.BodyParser(&request); err != nil {
		return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
	}
	if err := t.dal.SetRate(ctx.Context(), request.TicketID, request.Rate); err != nil {
		return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	return ctx.SendStatus(fiber.StatusOK)
}

type CloseTicketReq struct {
	TicketID string `json:"ticket"`
}
type CloseTicketResp struct {
	TicketID string `json:"ticket"`
}

func (t *TicketController) CloseTicket(ctx *fiber.Ctx) error {
	var request CloseTicketReq
	if err := ctx.BodyParser(&request); err != nil {
		return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
	}
	if err := t.dal.SetTicketStatus(ctx.Context(), request.TicketID, model.StateClose); err != nil {
		return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	// system error flag
	if _, err := t.dal.PutMessage(ctx.Context(), "close", "close", "close", request.TicketID, []string{}, false); err != nil {
		return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	return ctx.Status(fiber.StatusOK).JSON(CloseTicketResp{
		TicketID: request.TicketID,
	})
}

type ShownReq struct {
	ID string `json:"id"`
}

func (t *TicketController) SetShown(ctx *fiber.Ctx) error {
	var request ShownReq
	if err := ctx.BodyParser(&request); err != nil {
		return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
	}
	err := t.dal.SetShown(ctx.Context(), request.ID, "me")
	if err != nil {
		return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return ctx.SendStatus(fiber.StatusOK)
}

var _ tools.DataEmitter = (&TicketController{}).GetList

func (t *TicketController) GetList(ctx context.Context) chan interface{} {
	fmt.Println("GetList")
	defer func() {
		if rec := recover(); rec != nil {
			fmt.Println(rec)
		}
	}()
	sess := jwt_adapter.Get(ctx)
	fmt.Println("sess", sess)
	if sess == nil {
		return nil
	}

	output := make(chan interface{})

	if sess.Id == "" {
		go t.unauthorizedTickets(ctx, sess.Id, output)
	} else {
		role := jwt_adapter.GetRole(ctx)

		fmt.Println("ALL TICKETS Sess ", sess.Id, role)
		if role == "admin" || role == "manager" {
			go t.allTickets(ctx, output)
		} else {
			go t.userTickets(ctx, sess.Id, output)
		}
	}

	return output
}

func (t *TicketController) allTickets(ctx context.Context, output chan interface{}) {
	defer func() {
		if v := recover(); v != nil {
			fmt.Println("AllTicketsRec", v)
		}
	}()

	//data, count, err := h.dal.YieldTickets(ctx, 20)
	//
	//if err != nil {
	//	output <- errors.New("cannot get tickets:" + err.Error())
	//	return
	//}
	//
	//if data != nil {
	//	output <- GetTicketsResp{data, count}
	//}

	if err := t.dal.WatchAllTickets(ctx, func(ticket model.Ticket) error {
		msg, _ := json.Marshal(ticket)
		output <- msg
		return nil
	}); err != nil {
		output <- errors.New("cannot watch all tickets" + err.Error())
	}
}

func (t *TicketController) userTickets(ctx context.Context, userID string, output chan interface{}) {

	defer func() {
		if v := recover(); v != nil {
			fmt.Println("USERTICKS", v)
		}
	}()

	data, count, err := t.dal.YieldUserTickets(ctx, userID, 20, 0)

	if err != nil {
		output <- errors.New("cannot get tickets:" + err.Error())
		return
	}

	if data != nil {
		var filteredData []model.Ticket
		for _, ticket := range data {
			if !ticket.TopMessage.System {
				filteredData = append(filteredData, ticket)
			}
		}
		data = filteredData

		msg, _ := json.Marshal(GetTicketsResp{data, count})
		output <- msg
	}

	if err := t.dal.WatchTickets(ctx, userID, func(ticket model.Ticket) error {
		if ticket.TopMessage.System {
			return nil
		}
		msg, _ := json.Marshal(ticket)
		output <- msg
		return nil
	}); err != nil {
		output <- errors.New("cannot watch tickets")
		return
	}
}

func (t *TicketController) unauthorizedTickets(ctx context.Context, sess string, output chan interface{}) {
	//defer close(output)

	tickets, count, err := t.dal.GetTickets4Sess(ctx, sess)

	if err != nil {
		output <- errors.New("no tickets for session")
		return
	}

	if tickets != nil {
		msg, _ := json.Marshal(GetTicketsResp{tickets, count})
		output <- msg
	}
}

var _ tools.DataEmitter = (&TicketController{}).Subscribe

func (t *TicketController) Subscribe(ctx context.Context) chan interface{} {
	sess := jwt_adapter.Get(ctx)
	fmt.Println("SESS Subsc", sess)

	ticketID := ctx.Value(tools.ContextURLKey).(string)

	output := make(chan interface{})
	if sess.Id == "" {
		go func() {
			ticket, err := t.dal.GetTicket4Sess(ctx, ticketID, sess.Id)
			if err != nil || ticket == nil {
				output <- errors.New("no tickets 4 session")
				return
			}

			/*if err := h.dal.YieldMessages(ctx, ticketID, func(message model.Message) error {
				output <- message
				fmt.Println("OOOOOOLd")

				//if err := h.dal.SetShown(ctx, message.ID, sess.Session); err != nil {
				//
				//	output <- errors.New("cannot show message " + err.Error())
				//	return err
				//}

				return nil
			}); err != nil {
				output <- errors.New("cannot read messages " + err.Error())
			}*/

			if err := t.dal.WatchMessages(ctx, ticketID,
				func(message model.Message) error {
					if !message.System {
						msg, _ := json.Marshal(message)
						output <- msg
					}

					//if err := h.dal.SetShown(ctx, message.ID, sess.Session); err != nil {
					//	fmt.Println("3", err)
					//	output <- errors.New("cannot show watch message " + err.Error())
					//	return err
					//}

					return nil
				}); err != nil {
				fmt.Println("4", err)
				output <- errors.New("cannot watch messages " + err.Error())
			}
		}()
	} else {
		role := jwt_adapter.GetRole(ctx)

		if role == "admin" || role == "manager" {
			go func() {

				if err := t.dal.YieldMessages(ctx, ticketID, func(message model.Message) error {
					msg, _ := json.Marshal(message)
					output <- msg

					//if err := h.dal.SetShown(ctx, message.ID, sess.User); err != nil {
					//	fmt.Println("2", err)
					//	output <- errors.New("cannot show message " + err.Error())
					//	return err
					//}

					return nil
				}); err != nil {
					fmt.Println("1", err)
					output <- errors.New("cannot read messages " + err.Error())
				}

				if err := t.dal.WatchMessages(ctx, ticketID,
					func(message model.Message) error {
						msg, _ := json.Marshal(message)
						output <- msg

						//if err := h.dal.SetShown(ctx, message.ID, sess.Session); err != nil {
						//	fmt.Println("3", err)
						//	output <- errors.New("cannot show watch message " + err.Error())
						//	return err
						//}

						return nil
					}); err != nil {
					fmt.Println("4", err)
					output <- errors.New("cannot watch messages " + err.Error())
				}
			}()
		} else {
			go func() {
				defer func() {
					if v := recover(); v != nil {
						fmt.Println("heryvym panic", v)
					}
				}()
				ticket, err := t.dal.GetTicket4User(ctx, ticketID, sess.Id)
				if err != nil || ticket == nil {
					output <- errors.New("no tickets 4 user")
				}

				/*if err := h.dal.YieldMessages(ctx, ticketID, func(message model.Message) error {
					output <- message

					//if err := h.dal.SetShown(ctx, message.ID, sess.User); err != nil {
					//	fmt.Println("2", err)
					//	output <- errors.New("cannot show message " + err.Error())
					//	return err
					//}

					return nil
				}); err != nil {
					fmt.Println("1", err)
					output <- errors.New("cannot read messages " + err.Error())
				}*/

				if err := t.dal.WatchMessages(ctx, ticketID,
					func(message model.Message) error {
						if !message.System {
							msg, _ := json.Marshal(message)
							output <- msg
						}

						//if err := h.dal.SetShown(ctx, message.ID, sess.Session); err != nil {
						//	fmt.Println("3", err)
						//	output <- errors.New("cannot show watch message " + err.Error())
						//	return err
						//}

						return nil
					}); err != nil {
					fmt.Println("4", err)
					output <- errors.New("cannot watch messages " + err.Error())
				}
			}()
		}
	}

	return output
}
