package agent

import (
	"context"
	"errors"
	"fmt"

	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database"
	dberrors "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database/errors"
	dbtypes "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database/types"
	rmodel "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/request_model"
)

type CrmAgentService struct {
	dbClient database.Client
}

type CrmAgentServiceConfig struct {
	DbClient database.Client
}

func newCrmAgentService(cfg CrmAgentServiceConfig) *CrmAgentService {
	return &CrmAgentService{
		dbClient: cfg.DbClient,
	}
}

func (s *CrmAgentService) GetVacancyList(
	ctx context.Context,
	request *rmodel.VacancyListGetRequest,
) (*rmodel.VacancyListGetResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	resp, err := s.dbClient.GetVacancyList(ctx, &dbtypes.VacancyListGetRequest{
		Filters: &dbtypes.VacancyListFilters{
			DistributorId: request.Filters.DistributorId,
			CompanyId:     request.Filters.CompanyId,
			VacancyId:     request.Filters.VacancyId,
			Region:        request.Filters.Region,
			SalaryBottom:  request.Filters.SalaryBottom,
			SalaryTop:     request.Filters.SalaryTop,
			IsArchived:    request.Filters.IsArchived,
			Status:        dbtypes.NewVacancyStatus(request.Filters.Status.NullString()),
		},
		Page:     request.Page,
		PageSize: request.PageSize,
	})
	if err != nil {
		return nil, fmt.Errorf("%w: error getting vacancy list: %v", s.handleDBError(err), err)
	}

	result := new(rmodel.VacancyListGetResponse).From(resp)

	return result, nil
}

func (s *CrmAgentService) GetSubmissionList(
	ctx context.Context,
	request *rmodel.SubmissionListGetRequest,
) (*rmodel.SubmissionListGetResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	req := dbtypes.SubmissionListGetRequest{
		Filters: &dbtypes.SubmissionListGetFilters{
			AgentId: &request.AgentId,
		},
	}

	if request.Filters != nil {
		if request.Filters.VacancyId != nil {
			req.Filters.VacancyId = request.Filters.VacancyId
		}

		if request.Filters.Status != nil {
			req.Filters.Status = dbtypes.NewSubmissionStatus(request.Filters.Status.NullString())
		}
	}

	resp, err := s.dbClient.GetSubmissionList(ctx, &req)
	if err != nil {
		return nil, fmt.Errorf("%w: error getting submission list: %v", s.handleDBError(err), err)
	}

	result := new(rmodel.SubmissionListGetResponse).From(resp)

	return result, nil
}

func (s *CrmAgentService) CreateSubmission(
	ctx context.Context,
	request *rmodel.SubmissionCreateRequest,
) (*rmodel.SubmissionCreateResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	req := dbtypes.SubmissionCreateRequest{
		AgentId:   request.AgentId,
		VacancyId: request.VacancyId,
		CandidateInfo: &dbtypes.CandidateInfo{
			FirstName:   request.CandidateInfo.FirstName,
			LastName:    request.CandidateInfo.LastName,
			MiddleName:  request.CandidateInfo.MiddleName,
			PhoneNumber: request.CandidateInfo.PhoneNumber,
			Email:       request.CandidateInfo.Email,
			Birthday:    request.CandidateInfo.Birthday,
			CvLink:      request.CandidateInfo.CvLink,
			Resume:      request.CandidateInfo.Resume,
		},
	}

	resp, err := s.dbClient.CreateSubmission(ctx, &req)
	if err != nil {
		return nil, fmt.Errorf("%w: error creating submission: %v", s.handleDBError(err), err)
	}

	result := new(rmodel.SubmissionCreateResponse).From(resp)

	return result, nil
}

func (s *CrmAgentService) DeleteSubmission(
	ctx context.Context,
	request *rmodel.SubmissionDeleteRequest,
) (*rmodel.SubmissionDeleteResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	if _, err := s.dbClient.DeleteSubmission(ctx, &dbtypes.SubmissionDeleteRequest{
		Id: request.Id,
	}); err != nil {
		return nil, fmt.Errorf("%w: error deleting submission: %v", s.handleDBError(err), err)
	}

	return &rmodel.SubmissionDeleteResponse{}, nil
}

func (s *CrmAgentService) GetProfile(
	ctx context.Context,
	request *rmodel.ProfileGetRequest,
) (*rmodel.ProfileGetResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	resp, err := s.dbClient.GetProfileById(ctx, &dbtypes.ProfileGetRequest{
		Id: request.Id,
	})
	if err != nil {
		return nil, fmt.Errorf("%w: error getting agent info: %v", s.handleDBError(err), err)
	}

	result := new(rmodel.ProfileGetResponse).From(resp)

	return result, nil
}

func (s *CrmAgentService) UpdateProfile(
	ctx context.Context,
	request *rmodel.ProfileUpdateRequest,
) (*rmodel.ProfileUpdateResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	if _, err := s.dbClient.UpdateProfile(ctx, &dbtypes.ProfileUpdateRequest{
		Id:          request.Id,
		Name:        request.Name,
		PhoneNumber: request.PhoneNumber,
		Email:       request.Email,
	}); err != nil {
		return nil, fmt.Errorf("%w: error updating profile: %v", s.handleDBError(err), err)
	}

	return &rmodel.ProfileUpdateResponse{}, nil
}

func (s *CrmAgentService) GetCompanyList(
	ctx context.Context,
	request *rmodel.CompanyListGetRequest,
) (*rmodel.CompanyListGetResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	resp, err := s.dbClient.GetCompanyList(ctx, &dbtypes.CompanyListGetRequest{
		Id: request.Id,
	})
	if err != nil {
		return nil, fmt.Errorf("%w: error getting company list: %v", ErrInternal, err)
	}

	result := new(rmodel.CompanyListGetResponse).From(resp)

	return result, nil
}

func (s *CrmAgentService) GetCompanyInfo(
	ctx context.Context,
	request *rmodel.CompanyByIdGetRequest,
) (*rmodel.CompanyByIdGetResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	resp, err := s.dbClient.GetCompanyById(ctx, &dbtypes.CompanyByIdGetRequest{
		Id:        request.UserId,
		CompanyId: request.CompanyId,
	})
	if err != nil {
		return nil, fmt.Errorf("%w: error getting company info: %v", s.handleDBError(err), err)
	}

	result := new(rmodel.CompanyByIdGetResponse).From(resp)

	return result, nil
}

func (s *CrmAgentService) CreateCompany(
	ctx context.Context,
	request *rmodel.CompanyCreateRequest,
) (*rmodel.CompanyCreateResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	resp, err := s.dbClient.CreateCompany(ctx, &dbtypes.CompanyCreateRequest{
		OwnerId:         request.OwnerId,
		Name:            request.Name,
		LegalPerson:     request.LegalPerson,
		Description:     request.Description,
		Website:         request.Website,
		PhysicalAddress: request.PhysicalAddress,
		LegalAddress:    request.LegalAddress,
		Inn:             request.Inn,
		Kpp:             request.Kpp,
		Staff:           request.Staff,
	})
	if err != nil {
		return nil, fmt.Errorf("%w: error creating company: %v", s.handleDBError(err), err)
	}

	result := new(rmodel.CompanyCreateResponse).From(resp)

	return result, nil
}

func (s *CrmAgentService) UpdateCompanyInfo(
	ctx context.Context,
	request *rmodel.CompanyUpdateRequest,
) (*rmodel.CompanyUpdateResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	if _, err := s.dbClient.UpdateCompany(ctx, &dbtypes.CompanyUpdateRequest{
		Id:              request.Id,
		Name:            request.Name,
		LegalPerson:     request.LegalPerson,
		Description:     request.Description,
		Website:         request.Website,
		PhysicalAddress: request.PhysicalAddress,
		LegalAddress:    request.LegalAddress,
		Inn:             request.Inn,
		Kpp:             request.Kpp,
		Staff:           request.Staff,
	}); err != nil {
		return nil, fmt.Errorf("%w: error updating company info: %v", s.handleDBError(err), err)
	}

	return &rmodel.CompanyUpdateResponse{}, nil
}

func (s *CrmAgentService) GetBalance(
	ctx context.Context,
	request *rmodel.BalanceGetRequest,
) (*rmodel.BalanceGetResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	resp, err := s.dbClient.GetBalance(ctx, &dbtypes.BalanceGetRequest{
		OwnerId: request.OwnerId,
	})
	if err != nil {
		return nil, fmt.Errorf("%w: error getting balance: %v", s.handleDBError(err), err)
	}

	result := new(rmodel.BalanceGetResponse).From(resp)

	return result, nil
}

func (s *CrmAgentService) GetTransactionList(
	ctx context.Context,
	request *rmodel.TransactionListGetRequest,
) (*rmodel.TransactionListGetResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	req := dbtypes.TransactionListGetRequest{
		OwnerId:  request.OwnerId,
		Page:     request.Page,
		PageSize: request.PageSize,
	}

	if request.Filters != nil {
		req.Filters = new(dbtypes.TransactionListFilters)
		req.Filters.Type = dbtypes.NewTransactionType(request.Filters.Type.NullString())
		req.Filters.Status = dbtypes.NewTransactionStatus(request.Filters.Status.NullString())
		req.Filters.BankAccountId = request.Filters.BankAccountId
	}

	resp, err := s.dbClient.GetTransactionList(ctx, &req)
	if err != nil {
		return nil, fmt.Errorf("%w: error getting transactions: %v", s.handleDBError(err), err)
	}

	result := new(rmodel.TransactionListGetResponse).From(resp)

	return result, nil
}

func (s *CrmAgentService) CreateTransaction(
	ctx context.Context,
	request *rmodel.TransactionCreateRequest,
) (*rmodel.TransactionCreateResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	req := dbtypes.TransactionCreateRequest{
		OwnerId:       request.OwnerId,
		Amount:        request.Amount,
		Currency:      request.Currency,
		BankAccountId: request.BankAccountId,
		RequestId:     request.RequestId,
	}

	if request.Payload != nil {
		req.Payload = &dbtypes.TransactionPayload{
			Origin:      request.Payload.Origin,
			CompanyId:   request.Payload.CompanyId,
			CompanyName: request.Payload.CompanyName,
			VacancyId:   request.Payload.VacancyId,
			VacancyName: request.Payload.VacancyName,
		}
	}

	resp, err := s.dbClient.CreateTransaction(ctx, &req)
	if err != nil {
		return nil, fmt.Errorf("%w: error creating transaction: %v", s.handleDBError(err), err)
	}

	result := new(rmodel.TransactionCreateResponse).From(resp)

	return result, nil
}

func (s *CrmAgentService) GetBankAccountList(
	ctx context.Context,
	request *rmodel.BankAccountListGetRequest,
) (*rmodel.BankAccountListGetResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	resp, err := s.dbClient.GetBankAccountList(ctx, &dbtypes.BankAccountListGetRequest{
		OwnerId: request.OwnerId,
	})
	if err != nil {
		return nil, fmt.Errorf("%w: error getting bank accounts: %v", s.handleDBError(err), err)
	}

	result := new(rmodel.BankAccountListGetResponse).From(resp)

	return result, nil
}

func (s *CrmAgentService) CreateBankAccount(
	ctx context.Context,
	request *rmodel.BankAccountCreateRequest,
) (*rmodel.BankAccountCreateResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	resp, err := s.dbClient.CreateBankAccount(ctx, &dbtypes.BankAccountCreateRequest{
		OwnerId:              request.OwnerId,
		AccountNumber:        request.AccountNumber,
		AccountName:          request.AccountName,
		BankName:             request.BankName,
		Bik:                  request.Bik,
		CorrespondentAccount: request.CorrespondentAccount,
		IsPrimary:            request.IsPrimary,
	})
	if err != nil {
		return nil, fmt.Errorf("%w: error creating bank account: %v", s.handleDBError(err), err)
	}

	result := new(rmodel.BankAccountCreateResponse).From(resp)

	return result, nil
}

func (s *CrmAgentService) UpdateBankAccount(
	ctx context.Context,
	request *rmodel.BankAccountUpdateRequest,
) (*rmodel.BankAccountUpdateResponse, error) {
	if err := request.Validate(); err != nil {
		return nil, fmt.Errorf("%w: %v", ErrBadRequest, err)
	}

	if _, err := s.dbClient.UpdateBankAccount(ctx, &dbtypes.BankAccountUpdateRequest{
		Id:                   request.Id,
		AccountNumber:        request.AccountNumber,
		AccountName:          request.AccountName,
		BankName:             request.BankName,
		Bik:                  request.Bik,
		CorrespondentAccount: request.CorrespondentAccount,
		IsPrimary:            request.IsPrimary,
	}); err != nil {
		return nil, fmt.Errorf("%w: error updating bank account: %v", s.handleDBError(err), err)
	}

	return &rmodel.BankAccountUpdateResponse{}, nil
}

func (s *CrmAgentService) DeleteBankAccount(
	ctx context.Context,
	request *rmodel.BankAccountDeleteRequest,
) (*rmodel.BankAccountDeleteResponse, error) {
	return nil, ErrUnimplemented
}

func (s *CrmAgentService) handleDBError(err error) error {
	switch {
	case errors.Is(err, dberrors.ErrBadRequest):
		return ErrBadRequest
	case errors.Is(err, dberrors.ErrForbidden):
		return ErrForbidden
	case errors.Is(err, dberrors.ErrNotFound):
		return ErrNotFound
	case errors.Is(err, dberrors.ErrInternal):
		return ErrInternal
	default:
		return ErrInternal
	}
}
