package service

import (
	"context"
	"fmt"

	"git.kirlllll.ru/volontery/backend/internal/database"
	"git.kirlllll.ru/volontery/backend/internal/repository"
	"github.com/jackc/pgx/v5/pgtype"
)

// RequestService предоставляет методы для работы с заявками
type RequestService struct {
	requestRepo *repository.RequestRepository
}

// NewRequestService создает новый RequestService
func NewRequestService(requestRepo *repository.RequestRepository) *RequestService {
	return &RequestService{
		requestRepo: requestRepo,
	}
}

// CreateRequestInput - входные данные для создания заявки
type CreateRequestInput struct {
	RequesterID           int64   `json:"requester_id"`
	RequestTypeID         int64   `json:"request_type_id"`
	Title                 string  `json:"title"`
	Description           string  `json:"description"`
	Latitude              float64 `json:"latitude"`
	Longitude             float64 `json:"longitude"`
	Address               string  `json:"address"`
	City                  string  `json:"city,omitempty"`
	DesiredCompletionDate *string `json:"desired_completion_date,omitempty"`
	Urgency               string  `json:"urgency"`
	ContactPhone          string  `json:"contact_phone,omitempty"`
	ContactNotes          string  `json:"contact_notes,omitempty"`
}

// CreateRequest создает новую заявку
func (s *RequestService) CreateRequest(ctx context.Context, input CreateRequestInput) (*database.CreateRequestRow, error) {
	// Валидация
	if input.Title == "" {
		return nil, fmt.Errorf("title is required")
	}
	if input.Description == "" {
		return nil, fmt.Errorf("description is required")
	}
	if input.Latitude == 0 || input.Longitude == 0 {
		return nil, fmt.Errorf("location is required")
	}

	// Создание заявки
	return s.requestRepo.Create(ctx, database.CreateRequestParams{
		RequesterID:   input.RequesterID,
		RequestTypeID: input.RequestTypeID,
		Title:         input.Title,
		Description:   input.Description,
		StMakepoint:   input.Longitude,
		StMakepoint_2: input.Latitude,
		Address:       input.Address,
		City:          stringToPgText(input.City),
		Urgency:       stringToPgText(input.Urgency),
		ContactPhone:  stringToPgText(input.ContactPhone),
		ContactNotes:  stringToPgText(input.ContactNotes),
	})
}

// GetRequest получает заявку по ID
func (s *RequestService) GetRequest(ctx context.Context, id int64) (*database.GetRequestByIDRow, error) {
	return s.requestRepo.GetByID(ctx, id)
}

// GetUserRequests получает заявки пользователя
func (s *RequestService) GetUserRequests(ctx context.Context, userID int64, limit, offset int32) ([]database.GetRequestsByRequesterRow, error) {
	return s.requestRepo.GetByRequester(ctx, database.GetRequestsByRequesterParams{
		RequesterID: userID,
		Limit:       limit,
		Offset:      offset,
	})
}

// FindNearbyRequests ищет заявки рядом с точкой
func (s *RequestService) FindNearbyRequests(ctx context.Context, lat, lon float64, radiusMeters float64, statuses []database.RequestStatus, limit, offset int32) ([]database.FindRequestsNearbyRow, error) {
	// Конвертируем []RequestStatus в []string
	statusStrings := make([]string, len(statuses))
	for i, status := range statuses {
		statusStrings[i] = string(status)
	}

	return s.requestRepo.FindNearby(ctx, database.FindRequestsNearbyParams{
		StMakepoint:   lon,
		StMakepoint_2: lat,
		Column3:       statusStrings,
		StDwithin:     radiusMeters,
		Limit:         limit,
		Offset:        offset,
	})
}

// FindRequestsInBounds ищет заявки в прямоугольной области (для карты)
func (s *RequestService) FindRequestsInBounds(ctx context.Context, statuses []database.RequestStatus, minLon, minLat, maxLon, maxLat float64) ([]database.FindRequestsInBoundsRow, error) {
	// Конвертируем []RequestStatus в []string
	statusStrings := make([]string, len(statuses))
	for i, status := range statuses {
		statusStrings[i] = string(status)
	}

	return s.requestRepo.FindInBounds(ctx, database.FindRequestsInBoundsParams{
		Column1:          statusStrings,
		StMakeenvelope:   minLon,
		StMakeenvelope_2: minLat,
		StMakeenvelope_3: maxLon,
		StMakeenvelope_4: maxLat,
	})
}

// CreateVolunteerResponse создает отклик волонтера на заявку
func (s *RequestService) CreateVolunteerResponse(ctx context.Context, requestID, volunteerID int64, message string) (*database.VolunteerResponse, error) {
	return s.requestRepo.CreateVolunteerResponse(ctx, database.CreateVolunteerResponseParams{
		RequestID:   requestID,
		VolunteerID: volunteerID,
		Message:     stringToPgText(message),
	})
}

// GetRequestResponses получает отклики на заявку
func (s *RequestService) GetRequestResponses(ctx context.Context, requestID int64) ([]database.GetResponsesByRequestRow, error) {
	return s.requestRepo.GetResponsesByRequest(ctx, requestID)
}

// ListRequestTypes получает список типов заявок
func (s *RequestService) ListRequestTypes(ctx context.Context) ([]database.RequestType, error) {
	return s.requestRepo.ListTypes(ctx)
}

// GetPendingModerationRequests получает заявки на модерации
func (s *RequestService) GetPendingModerationRequests(ctx context.Context, limit, offset int32) ([]database.GetPendingModerationRequestsRow, error) {
	return s.requestRepo.GetPendingModerationRequests(ctx, limit, offset)
}

// ApproveRequest одобряет заявку
func (s *RequestService) ApproveRequest(ctx context.Context, requestID, moderatorID int64, comment *string) error {
	moderationComment := stringToPgText("")
	if comment != nil {
		moderationComment = stringToPgText(*comment)
	}

	return s.requestRepo.ApproveRequest(ctx, database.ApproveRequestParams{
		ID: requestID,
		ModeratedBy: pgtype.Int8{
			Int64: moderatorID,
			Valid: true,
		},
		ModerationComment: moderationComment,
	})
}

// RejectRequest отклоняет заявку
func (s *RequestService) RejectRequest(ctx context.Context, requestID, moderatorID int64, comment string) error {
	if comment == "" {
		return fmt.Errorf("rejection comment is required")
	}

	return s.requestRepo.RejectRequest(ctx, database.RejectRequestParams{
		ID: requestID,
		ModeratedBy: pgtype.Int8{
			Int64: moderatorID,
			Valid: true,
		},
		ModerationComment: stringToPgText(comment),
	})
}

// GetModeratedRequests получает заявки, модерированные указанным модератором
func (s *RequestService) GetModeratedRequests(ctx context.Context, moderatorID int64, limit, offset int32) ([]database.GetModeratedRequestsRow, error) {
	return s.requestRepo.GetModeratedRequests(ctx, moderatorID, limit, offset)
}

// AcceptVolunteerResponse принимает отклик волонтера через хранимую процедуру
func (s *RequestService) AcceptVolunteerResponse(ctx context.Context, responseID, requesterID int64) (*database.CallAcceptVolunteerResponseRow, error) {
	return s.requestRepo.AcceptVolunteerResponse(ctx, responseID, requesterID)
}

// CompleteRequestWithRating завершает заявку с рейтингом через хранимую процедуру
func (s *RequestService) CompleteRequestWithRating(ctx context.Context, requestID, requesterID int64, rating int32, comment *string) (*database.CallCompleteRequestWithRatingRow, error) {
	if rating < 1 || rating > 5 {
		return nil, fmt.Errorf("rating must be between 1 and 5")
	}
	return s.requestRepo.CompleteRequestWithRating(ctx, requestID, requesterID, rating, comment)
}

// ModerateRequestProcedure модерирует заявку через хранимую процедуру
func (s *RequestService) ModerateRequestProcedure(ctx context.Context, requestID, moderatorID int64, action string, comment *string) (*database.CallModerateRequestRow, error) {
	if action != "approve" && action != "reject" {
		return nil, fmt.Errorf("action must be 'approve' or 'reject'")
	}
	if action == "reject" && (comment == nil || *comment == "") {
		return nil, fmt.Errorf("comment is required when rejecting")
	}
	return s.requestRepo.ModerateRequestProcedure(ctx, requestID, moderatorID, action, comment)
}
