package http_router

import (
	"context"
	"encoding/json"
	"fmt"
	"log/slog"

	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/feed"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/types"
	pbfAgent "github.com/AlexOreL-272/ProtoMolva/go/gen/agent"
	pbfDistr "github.com/AlexOreL-272/ProtoMolva/go/gen/distributor"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
)

//nolint:unused // TODO: review feed handlers
func (h *handler) createCreateCompanyFeedEvent(ctx context.Context, uid string, isAgent bool, resp []byte, handlerName string) {
	var (
		jsonResp    interface{}
		companyID   string
		companyName string
	)

	if isAgent {
		jsonResp = new(pbfAgent.SetCompanyDataResponse)
	} else {
		jsonResp = new(pbfDistr.SetCompanyDataResponse)
	}

	ownerType := feed.RoleDistributor
	if isAgent {
		ownerType = feed.RoleAgent
	}

	if err := json.Unmarshal(resp, jsonResp); err != nil {
		h.logger.Error(fmt.Sprintf("failed to unmarshal %s SetCompanyDataResponse", ownerType),
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	switch resp := jsonResp.(type) {
	case *pbfAgent.SetCompanyDataResponse:
		company := resp.GetCompany()
		companyID = company.GetCompanyId()
		companyName = company.GetName()

	case *pbfDistr.SetCompanyDataResponse:
		company := resp.GetCompany()
		companyID = company.GetCompanyId()
		companyName = company.GetName()

	default:
		h.logger.Error("unexpected response type",
			slog.String("handler", handlerName))

		return
	}

	event := feed.Event{
		OwnerId:    uid,
		OwnerType:  ownerType,
		EventType:  feed.EventCompanyCreated,
		Message:    fmt.Sprintf("Компания %s создана", companyName),
		Visibility: feed.VisibilityCompanyWide,
		CompanyID:  &companyID,
		Payload: feed.EventPayload{
			AttachmentId:   companyID,
			AttachmentType: feed.AttachmentTypeCompany,
		},
	}

	if err := h.feed.CreateEvent(ctx, &event); err != nil {
		h.logger.Error("failed to create feed event",
			slog.String("uid", uid),
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
			slog.String("company_id", companyID),
			slog.String("company_id", companyID),
		)
	}
}

//nolint:unused // TODO: review feed handlers
func (h *handler) createAddNewStaffMemberFeedEvent(ctx context.Context, uid string, isAgent bool, newStaffMember, companyId, handlerName string) {
	ownerType := feed.RoleDistributor

	if isAgent {
		ownerType = feed.RoleAgent
	}

	event := feed.Event{
		OwnerId:    uid,
		OwnerType:  ownerType,
		EventType:  feed.EventNewCompanyMember,
		Message:    "Новый сотрудник добавлен в компанию",
		Visibility: feed.VisibilityCompanyWide,
		CompanyID:  &companyId,
		Payload: feed.EventPayload{
			AttachmentId:   newStaffMember,
			AttachmentType: feed.AttachmentTypeProfile,
		},
	}

	if err := h.feed.CreateEvent(ctx, &event); err != nil {
		h.logger.Error("failed to create feed event",
			slog.String("uid", uid),
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
			slog.String("company_id", companyId),
			slog.String("new_staff_member", newStaffMember),
		)
	}
}

//nolint:unused // TODO: review feed handlers
func (h *handler) createSetSubmissionStatusFeedEvent(ctx context.Context, distributorId, submissionId string, submissionStatus types.Status, handlerName string) {
	var message string

	switch submissionStatus {
	case types.StatusAccepted:
		message = "Отклик на вакансию принят"
	case types.StatusDeclined:
		message = "Отклик на вакансию отклонен"
	case types.StatusSuspended:
		message = "Отклик на вакансию отложен"
	default:
		message = "Статус отклика на вакансию был изменен"
	}

	companyIds, err := h.feed.GetCompanyIdByUidTmp(ctx, distributorId)
	if err != nil {
		h.logger.Error("failed to get company Id",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
			slog.String("distributor_id", distributorId))
	}

	agentId, err := h.feed.GetAgentIdBySubmissionId(ctx, submissionId)
	if err != nil {
		//я хз стоит ли отдельно как-то обрабатывать ErrorNotFound(вроде как нет)
		h.logger.Error("failed to get agent Id",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
			slog.String("submission_id", submissionId))
	}

	for _, companyId := range companyIds {
		event := feed.Event{
			OwnerId:    distributorId,
			OwnerType:  feed.RoleDistributor,
			EventType:  feed.EventSubmissionStatusChanged,
			Message:    message,
			Visibility: feed.VisibilityCompanyWide,
			CompanyID:  &companyId,
			Payload: feed.EventPayload{
				AttachmentId:       submissionId,
				AttachmentType:     feed.AttachmentTypeCV,
				AdditionalReceiver: agentId,
				CustomData:         submissionStatus.String(),
			},
		}

		if err := h.feed.CreateEvent(ctx, &event); err != nil {
			h.logger.Error("failed to create feed event",
				slog.String("distributor_id", distributorId),
				slog.String("agent_id", agentId),
				slog.String("error", err.Error()),
				slog.String("handler", handlerName),
				slog.String("submission_id", submissionId),
				slog.String("company_id", companyId),
			)
		}
	}
}

//nolint:unused // TODO: review feed handlers
func (h *handler) createTransactionFeedEvent(ctx context.Context, uid string, isAgent bool, amount int64, currency, bankAccountId, handlerName string) {
	var message string

	if amount > 0 {
		message = fmt.Sprintf("Вы получили %.2f %s", float64(amount)/100, currency)
	} else {
		message = fmt.Sprintf("С вашего аккаунта выведено %.2f %s", float64(-amount)/100, currency)
	}

	ownerType := feed.RoleDistributor

	if isAgent {
		ownerType = feed.RoleAgent
	}

	event := feed.Event{
		OwnerId:    uid,
		OwnerType:  ownerType,
		EventType:  feed.EventTransactionCreated,
		Message:    message,
		Visibility: feed.VisibilityPrivate,
		Payload: feed.EventPayload{
			AttachmentId:   bankAccountId,
			AttachmentType: feed.AttachmentTypeBankAccount,
		},
	}

	if err := h.feed.CreateEvent(ctx, &event); err != nil {
		h.logger.Error("failed to create feed event",
			slog.String("error", err.Error()),
			slog.String("uid", uid),
			slog.String("handler", handlerName),
			slog.String("bank_account_id", bankAccountId))
	}
}

//nolint:unused // TODO: review feed handlers
func (h *handler) createCreateBankAccountFeedEvent(ctx context.Context, uid string, isAgent bool, resp []byte, bankName, handlerName string) {
	var msg proto.Message

	var ownerType feed.UserRole

	if isAgent {
		msg = &pbfAgent.CreateBankAccountResponse{}
		ownerType = feed.RoleAgent
	} else {
		msg = &pbfDistr.CreateBankAccountResponse{}
		ownerType = feed.RoleDistributor
	}

	if err := protojson.Unmarshal(resp, msg); err != nil {
		h.logger.Error(fmt.Sprintf("failed to unmarshal %s CreateBankAccountResponse", ownerType),
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	var bankAccountId string
	switch v := msg.(type) {
	case *pbfAgent.CreateBankAccountResponse:
		bankAccountId = v.GetBankAccountId()
	case *pbfDistr.CreateBankAccountResponse:
		bankAccountId = v.GetBankAccountId()
	default:
		h.logger.Error("unexpected response type",
			slog.String("handler", handlerName),
		)

		return
	}

	event := feed.Event{
		OwnerId:    uid,
		OwnerType:  ownerType,
		EventType:  feed.EventBankAccountCreated,
		Message:    fmt.Sprintf("Банковский счёт %s создан", bankName),
		Visibility: feed.VisibilityPrivate,
		Payload: feed.EventPayload{
			AttachmentId:   bankAccountId,
			AttachmentType: feed.AttachmentTypeBankAccount,
		},
	}

	if err := h.feed.CreateEvent(ctx, &event); err != nil {
		h.logger.Error("failed to create feed event",
			slog.String("uid", uid),
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
			slog.String("bank_account_id", bankAccountId),
		)
	}
}

func (h *handler) createEditBankAccountFeedEvent(ctx context.Context, uid string, isAgent bool, bankAccountID, handlerName string) {
	ownerType := feed.RoleDistributor

	if isAgent {
		ownerType = feed.RoleAgent
	}

	event := feed.Event{
		OwnerId:    uid,
		OwnerType:  ownerType,
		EventType:  feed.EventBankAccountChanged,
		Message:    "Банковский счёт обновлён",
		Visibility: feed.VisibilityPrivate,
		Payload: feed.EventPayload{
			AttachmentId:   bankAccountID,
			AttachmentType: feed.AttachmentTypeBankAccount,
		},
	}

	if err := h.feed.CreateEvent(ctx, &event); err != nil {
		h.logger.Error("failed to create feed event",
			slog.String("uid", uid),
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
			slog.String("bank_account_id", bankAccountID))
	}
}

func (h *handler) createEditCompanyFeedEvent(ctx context.Context, uid string, isAgent bool, companyID, handlerName string) {
	ownerType := feed.RoleDistributor

	if isAgent {
		ownerType = feed.RoleAgent
	}

	event := feed.Event{
		OwnerId:    uid,
		OwnerType:  ownerType,
		EventType:  feed.EventCompanyChanged,
		Message:    "Данные компании обновлены",
		Visibility: feed.VisibilityCompanyWide,
		CompanyID:  &companyID,
		Payload: feed.EventPayload{
			AttachmentId:   companyID,
			AttachmentType: feed.AttachmentTypeCompany,
		},
	}

	if err := h.feed.CreateEvent(ctx, &event); err != nil {
		h.logger.Error("failed to create feed event",
			slog.String("uid", uid),
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
			slog.String("company_id", companyID))
	}
}

func (h *handler) createSetProfileEvent(ctx context.Context, uid string, isAgent bool, handlerName string) {
	ownerType := feed.RoleDistributor

	if isAgent {
		ownerType = feed.RoleAgent
	}

	event := feed.Event{
		OwnerId:    uid,
		OwnerType:  ownerType,
		EventType:  feed.EventProfileChanged,
		Message:    "Ваши данные профиля обновлены",
		Visibility: feed.VisibilityPrivate,
		Payload: feed.EventPayload{
			AttachmentId:   uid,
			AttachmentType: feed.AttachmentTypeProfile,
		},
	}

	if err := h.feed.CreateEvent(ctx, &event); err != nil {
		h.logger.Error("failed to create feed event",
			slog.String("uid", uid),
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) createSetDistributorVacancyEvent(ctx context.Context, distributorId, vacancyId, vacancyName, handlerName string) {
	event := feed.Event{
		OwnerId:    distributorId,
		OwnerType:  feed.RoleDistributor,
		EventType:  feed.EventVacancyCreated,
		Message:    fmt.Sprintf("Вакансия %s была создана", vacancyName),
		Visibility: feed.VisibilityPublic,
		Payload: feed.EventPayload{
			AttachmentId:   vacancyId,
			AttachmentType: feed.AttachmentTypeVacancy,
		},
	}

	if err := h.feed.CreateEvent(ctx, &event); err != nil {
		h.logger.Error("failed to create feed event",
			slog.String("distributor_id", distributorId),
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
			slog.String("vacancy_id", vacancyId),
		)
	}
}

func (h *handler) createEditDistributorVacancyEvent(ctx context.Context, distributorId, vacancyId, handlerName string) {
	event := feed.Event{
		OwnerId:    distributorId,
		OwnerType:  feed.RoleDistributor,
		EventType:  feed.EventVacancyChanged,
		Message:    "Вакансия была изменена",
		Visibility: feed.VisibilityPublic,
		Payload: feed.EventPayload{
			AttachmentId:   vacancyId,
			AttachmentType: feed.AttachmentTypeVacancy,
		},
	}

	if err := h.feed.CreateEvent(ctx, &event); err != nil {
		h.logger.Error("failed to create feed event",
			slog.String("distributor_id", distributorId),
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
			slog.String("vacancy_id", vacancyId))
	}
}

func (h *handler) createSendVacancyToModerationEvent(ctx context.Context, distributorId, vacancyId, handlerName string) {
	companyIds, err := h.feed.GetCompanyIdByUidTmp(ctx, distributorId)
	if err != nil {
		h.logger.Error("failed to get company Id",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
			slog.String("distributor_id", distributorId))
	}

	for _, companyId := range companyIds {
		event := feed.Event{
			OwnerId:    distributorId,
			OwnerType:  feed.RoleDistributor,
			EventType:  feed.EventVacancyModerationSent,
			Message:    "Вакансия была отправлена на модерацию",
			Visibility: feed.VisibilityCompanyWide,
			CompanyID:  &companyId,
			Payload: feed.EventPayload{
				AttachmentId:   vacancyId,
				AttachmentType: feed.AttachmentTypeVacancy,
			},
		}

		if err := h.feed.CreateEvent(ctx, &event); err != nil {
			h.logger.Error("failed to create feed event",
				slog.String("distributor_id", distributorId),
				slog.String("error", err.Error()),
				slog.String("handler", handlerName),
				slog.String("vacancy_id", vacancyId),
				slog.String("company_id", companyId),
			)
		}
	}
}

func (h *handler) createPostAnketaEvent(ctx context.Context, agentID, submissionId, handlerName string) {
	companyIds, err := h.feed.GetCompanyIdByUidTmp(ctx, agentID)
	if err != nil {
		h.logger.Error("failed to get company Id",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
			slog.String("agent_id", agentID))
	}

	for _, companyId := range companyIds {
		event := feed.Event{
			OwnerId:    agentID,
			OwnerType:  feed.RoleAgent,
			EventType:  feed.EventPostAnketa,
			Message:    "Анкета была заполнена",
			Visibility: feed.VisibilityPrivate,
			CompanyID:  &companyId,
			Payload: feed.EventPayload{
				AttachmentId:   submissionId,
				AttachmentType: feed.AttachmentTypeSubmission,
			},
		}

		if err := h.feed.CreateEvent(ctx, &event); err != nil {
			h.logger.Error("failed to create feed event",
				slog.String("agent_id", agentID),
				slog.String("error", err.Error()),
				slog.String("handler", handlerName),
				slog.String("submission_id", submissionId),
				slog.String("company_id", companyId),
			)
		}
	}
}
