package http_router

import (
	"context"
	"encoding/json"
	"io"
	"log/slog"
	"net/http"

	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/constants"
	rmodel "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/request_model"
	"github.com/gorilla/mux"
)

func (h *handler) getBalanceAgentHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "getBalanceAgentHandler"

	var (
		vars    = mux.Vars(r)
		agentId = vars["agent_id"]
	)

	result, err := h.agentService.GetBalance(r.Context(), &rmodel.BalanceGetRequest{
		OwnerId: agentId,
	})
	if err != nil {
		h.handleAgentError(w, err)
		h.logger.Error("error getting balance info",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) getTransactionListAgentHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "getTransactionListAgentHandler"

	var (
		vars    = mux.Vars(r)
		agentId = vars["agent_id"]
	)

	request, err := new(rmodel.TransactionListGetRequest).FromQuery(r.URL.Query())
	if err != nil {
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
		h.logger.Error("error parsing request: ",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName))

		return
	}

	request.OwnerId = agentId

	result, err := h.agentService.GetTransactionList(r.Context(), request)
	if err != nil {
		h.handleAgentError(w, err)
		h.logger.Error("error getting transactions",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) createTransactionAgentHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "createTransactionAgentHandler"

	var (
		vars    = mux.Vars(r)
		agentId = vars["agent_id"]
	)

	var request rmodel.TransactionCreateRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
		h.logger.Error("error while unmarshalling request: ",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName))

		return
	}

	defer func(body io.ReadCloser) {
		if err := body.Close(); err != nil {
			http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
			h.logger.Error("error closing body",
				slog.String("error", err.Error()),
				slog.String("handler", handlerName))
		}
	}(r.Body)

	request.OwnerId = agentId

	result, err := h.agentService.CreateTransaction(r.Context(), &request)
	if err != nil {
		h.handleAgentError(w, err)
		h.logger.Error("error creating transaction",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	// TODO: fix feed event
	// go h.createTransactionFeedEvent(
	// 	context.Background(),
	// 	agentId,
	// 	true,
	// 	request.Amount,
	// 	request.Currency,
	// 	request.BankAccountId,
	// 	handlerName,
	// )

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) getBankAccountListAgentHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "getBankAccountListAgentHandler"

	var (
		vars    = mux.Vars(r)
		agentId = vars["agent_id"]
	)

	result, err := h.agentService.GetBankAccountList(r.Context(), &rmodel.BankAccountListGetRequest{
		OwnerId: agentId,
	})
	if err != nil {
		h.handleAgentError(w, err)
		h.logger.Error("error getting bank accounts",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) createBankAccountAgentHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "createBankAccountAgentHandler"

	var (
		vars    = mux.Vars(r)
		agentId = vars["agent_id"]
	)

	var request rmodel.BankAccountCreateRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
		h.logger.Error("error while unmarshalling request: ",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName))

		return
	}

	defer func(body io.ReadCloser) {
		if err := body.Close(); err != nil {
			http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
			h.logger.Error("error closing body",
				slog.String("error", err.Error()),
				slog.String("handler", handlerName))
		}
	}(r.Body)

	request.OwnerId = agentId

	result, err := h.agentService.CreateBankAccount(r.Context(), &request)
	if err != nil {
		h.handleAgentError(w, err)
		h.logger.Error("error creating bank account",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	// FIXME: possible nil dereference
	// h.createCreateBankAccountFeedEvent(r.Context(), agentId, true, resp, *request.BankName, handlerName)

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) updateBankAccountAgentHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "updateBankAccountAgentHandler"

	var (
		vars          = mux.Vars(r)
		agentId       = vars["agent_id"]
		bankAccountId = vars["bank_account_id"]
	)

	var request rmodel.BankAccountUpdateRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
		h.logger.Error("error while unmarshalling request: ",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName))

		return
	}

	defer func(body io.ReadCloser) {
		if err := body.Close(); err != nil {
			http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
			h.logger.Error("error closing body",
				slog.String("error", err.Error()),
				slog.String("handler", handlerName))
		}
	}(r.Body)

	request.Id = bankAccountId
	request.OwnerId = agentId

	if _, err := h.agentService.UpdateBankAccount(r.Context(), &request); err != nil {
		h.handleAgentError(w, err)
		h.logger.Error("error editing bank account",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	go h.createEditBankAccountFeedEvent(
		context.Background(),
		agentId,
		true,
		bankAccountId,
		handlerName,
	)

	w.WriteHeader(http.StatusNoContent)
}

// TODO: test when implemented
func (h *handler) deleteBankAccountAgentHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "deleteBankAccountAgentHandler"

	var (
		vars          = mux.Vars(r)
		agentId       = vars["agent_id"]
		bankAccountId = vars["bank_account_id"]
	)

	result, err := h.agentService.DeleteBankAccount(r.Context(), &rmodel.BankAccountDeleteRequest{
		Id:      bankAccountId,
		OwnerId: agentId,
	})
	if err != nil {
		h.handleAgentError(w, err)
		h.logger.Error("error deleting bank account",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	go func() {
		if err := h.feed.CancelEvent(context.Background(), bankAccountId, "Банковский счёт удален"); err != nil {
			h.logger.Error("error cancelling event: ",
				slog.String("error", err.Error()),
				slog.String("handler", handlerName))
		}
	}()

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) getBalanceDistributorHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "getBalanceDistributorHandler"

	var (
		vars   = mux.Vars(r)
		distId = vars["distributor_id"]
	)

	result, err := h.distributorService.GetBalance(r.Context(), &rmodel.BalanceGetRequest{
		OwnerId: distId,
	})
	if err != nil {
		h.handleDistributorError(w, err)
		h.logger.Error("error getting balance",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) getCompanyBalanceDistributorHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "getCompanyBalanceDistributorHandler"

	var (
		vars      = mux.Vars(r)
		companyId = vars["company_id"]
	)

	result, err := h.distributorService.GetBalance(r.Context(), &rmodel.BalanceGetRequest{
		OwnerId: companyId,
	})
	if err != nil {
		h.handleDistributorError(w, err)
		h.logger.Error("error getting balance",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) getTransactionListDistributorHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "getTransactionListDistributorHandler"

	var (
		vars   = mux.Vars(r)
		distId = vars["distributor_id"]
	)

	request, err := new(rmodel.TransactionListGetRequest).FromQuery(r.URL.Query())
	if err != nil {
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
		h.logger.Error("error parsing request: ",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	request.OwnerId = distId

	result, err := h.distributorService.GetTransactionList(r.Context(), request)
	if err != nil {
		h.handleDistributorError(w, err)
		h.logger.Error("error getting transactions",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) getCompanyTransactionListDistributorHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "getCompanyTransactionListDistributorHandler"

	var (
		vars      = mux.Vars(r)
		companyId = vars["company_id"]
	)

	request, err := new(rmodel.TransactionListGetRequest).FromQuery(r.URL.Query())
	if err != nil {
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
		h.logger.Error("error parsing request: ",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	request.OwnerId = companyId

	result, err := h.distributorService.GetTransactionList(r.Context(), request)
	if err != nil {
		h.handleDistributorError(w, err)
		h.logger.Error("error getting transactions",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) createTransactionDistributorHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "createTransactionDistributorHandler"

	var (
		vars   = mux.Vars(r)
		distId = vars["distributor_id"]
	)

	var request rmodel.TransactionCreateRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
		h.logger.Error("error while unmarshalling request: ",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName))

		return
	}

	defer func(body io.ReadCloser) {
		if err := body.Close(); err != nil {
			http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
			h.logger.Error("error closing body",
				slog.String("error", err.Error()),
				slog.String("handler", handlerName))
		}
	}(r.Body)

	request.OwnerId = distId

	result, err := h.distributorService.CreateTransaction(r.Context(), &request)
	if err != nil {
		h.handleDistributorError(w, err)
		h.logger.Error("error creating transaction",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	// TODO: fix feed event
	// go h.createTransactionFeedEvent(
	// 	context.Background(),
	// 	distId,
	// 	false,
	// 	request.Amount,
	// 	request.Currency,
	// 	request.BankAccountId,
	// 	handlerName,
	// )

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) getBankAccountListDistributorHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "getBankAccountListDistributorHandler"

	var (
		vars   = mux.Vars(r)
		distId = vars["distributor_id"]
	)

	result, err := h.distributorService.GetBankAccountList(r.Context(), &rmodel.BankAccountListGetRequest{
		OwnerId: distId,
	})
	if err != nil {
		h.handleDistributorError(w, err)
		h.logger.Error("error getting bank accounts",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) getCompanyBankAccountListDistributorHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "getCompanyBankAccountListDistributorHandler"

	var (
		vars      = mux.Vars(r)
		companyId = vars["company_id"]
	)

	result, err := h.distributorService.GetBankAccountList(r.Context(), &rmodel.BankAccountListGetRequest{
		OwnerId: companyId,
	})
	if err != nil {
		h.handleDistributorError(w, err)
		h.logger.Error("error getting bank accounts",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) createBankAccountDistributorHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "createBankAccountDistributorHandler"

	var (
		vars      = mux.Vars(r)
		companyId = vars["company_id"]
	)

	var request rmodel.BankAccountCreateRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
		h.logger.Error("error while unmarshalling request: ",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName))

		return
	}

	defer func(body io.ReadCloser) {
		if err := body.Close(); err != nil {
			http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
			h.logger.Error("error closing body",
				slog.String("error", err.Error()),
				slog.String("handler", handlerName))
		}
	}(r.Body)

	request.OwnerId = companyId

	result, err := h.distributorService.CreateBankAccount(r.Context(), &request)
	if err != nil {
		h.handleDistributorError(w, err)
		h.logger.Error("error creating bank account",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	// FIXME: possible nil dereference
	// go h.createCreateBankAccountFeedEvent(
	// 	context.Background(),
	// 	distId,
	// 	false,
	// 	resp,
	// 	*request.BankName,
	// 	handlerName,
	// )

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) updateBankAccountDistributorHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "updateBankAccountDistributorHandler"

	var (
		vars          = mux.Vars(r)
		distId        = vars["distributor_id"]
		bankAccountId = vars["bank_account_id"]
	)

	var request rmodel.BankAccountUpdateRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
		h.logger.Error("error while unmarshalling request: ",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName))

		return
	}

	defer func(body io.ReadCloser) {
		if err := body.Close(); err != nil {
			http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
			h.logger.Error("error closing body",
				slog.String("error", err.Error()),
				slog.String("handler", handlerName))
		}
	}(r.Body)

	request.OwnerId = distId
	request.Id = bankAccountId

	result, err := h.distributorService.UpdateBankAccount(r.Context(), &request)
	if err != nil {
		h.handleDistributorError(w, err)
		h.logger.Error("error editing bank account",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	go h.createEditBankAccountFeedEvent(
		context.Background(),
		distId,
		true,
		bankAccountId,
		handlerName,
	)

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}

func (h *handler) deleteBankAccountDistributorHandler(w http.ResponseWriter, r *http.Request) {
	const handlerName = "deleteBankAccountDistributorHandler"

	var (
		vars          = mux.Vars(r)
		distId        = vars["distributor_id"]
		bankAccountId = vars["bank_account_id"]
	)

	result, err := h.distributorService.DeleteBankAccount(r.Context(), &rmodel.BankAccountDeleteRequest{
		Id:      bankAccountId,
		OwnerId: distId,
	})
	if err != nil {
		h.handleDistributorError(w, err)
		h.logger.Error("error deleting bank account",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)

		return
	}

	go func() {
		if err := h.feed.CancelEvent(context.Background(), bankAccountId, "Банковский счёт удален"); err != nil {
			http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
			h.logger.Error("error while cancelling event: ",
				slog.String("error", err.Error()),
				slog.String("handler", handlerName))
		}
	}()

	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(result); err != nil {
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("error encoding response",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName),
		)
	}
}
