package scenarios_service

import (
	"context"
	"errors"
	"evening_detective_server/internal/modules/cleaner"
	"evening_detective_server/internal/modules/storytelling"
	"evening_detective_server/internal/repos/scenarios_repo"
	"strings"
)

type ScenarioService struct {
	scenariosRepo *scenarios_repo.ScenariosRepo
	cleaner       cleaner.ICleaner
	domain        string
}

func NewScenarioService(
	scenariosRepo *scenarios_repo.ScenariosRepo,
	cleaner cleaner.ICleaner,
	domain string,
) *ScenarioService {
	return &ScenarioService{
		scenariosRepo: scenariosRepo,
		cleaner:       cleaner,
		domain:        domain,
	}
}

func (s *ScenarioService) AddScenario(
	ctx context.Context,
	name string,
	authorId int,
) (int, error) {
	id, err := s.scenariosRepo.AddScenario(ctx, name, authorId)
	if err != nil {
		return 0, err
	}
	return id, nil
}

func (s *ScenarioService) GetScenariosByAuthorID(
	ctx context.Context,
	authorId int,
) ([]*Scenario, error) {
	scenarios, err := s.scenariosRepo.GetScenariosByAuthorID(ctx, authorId)
	if err != nil {
		return nil, err
	}
	return mapScenariosLight(scenarios, s.domain), nil
}

func (s *ScenarioService) GetScenariosCatalog(
	ctx context.Context,
) ([]*Scenario, error) {
	scenarios, err := s.scenariosRepo.GetScenariosByStatus(ctx, "public")
	if err != nil {
		return nil, err
	}
	return mapScenariosLight(scenarios, s.domain), nil
}

func (s *ScenarioService) GetFullScenarioByID(
	ctx context.Context,
	id int,
) (*Scenario, error) {
	scenario, err := s.scenariosRepo.GetScenarioByID(ctx, id)
	if err != nil {
		return nil, err
	}
	return mapScenario(scenario, s.domain)
}

func (s *ScenarioService) GetScenarioByID(
	ctx context.Context,
	id int,
) (*Scenario, error) {
	scenario, err := s.scenariosRepo.GetScenarioByID(ctx, id)
	if err != nil {
		return nil, err
	}
	return MapScenarioLight(scenario, s.domain), nil
}

func (s *ScenarioService) UpdateScenarioByID(
	ctx context.Context,
	id int,
	name string,
	description string,
	image string,
) error {
	return s.scenariosRepo.UpdateScenarioByID(ctx, id, name, description, image)
}

func (s *ScenarioService) UpdateScenarioStatusByID(
	ctx context.Context,
	id int,
	status string,
) error {
	return s.scenariosRepo.UpdateScenarioStatusByID(ctx, id, status)
}

func (s *ScenarioService) DeleteScenarioByID(
	ctx context.Context,
	id int,
) error {
	return s.scenariosRepo.DeleteScenarioByID(ctx, id)
}

func (s *ScenarioService) AddScenarioPlace(
	ctx context.Context,
	id int,
	place *storytelling.Place,
) error {
	story, err := s.getStory(ctx, id)
	if err != nil {
		return err
	}
	story.Places = append(
		[]*storytelling.Place{place},
		story.Places...,
	)
	return s.updateStory(ctx, id, story)
}

func (s *ScenarioService) UpdateScenarioPlace(
	ctx context.Context,
	id int,
	code string,
	place *storytelling.Place,
) error {
	story, err := s.getStory(ctx, id)
	if err != nil {
		return err
	}
	for i := range story.Places {
		if story.Places[i].Code == code {
			story.Places[i] = place
			break
		}
	}
	return s.updateStory(ctx, id, story)
}

func (s *ScenarioService) DeleteScenarioPlace(
	ctx context.Context,
	id int,
	code string,
) error {
	story, err := s.getStory(ctx, id)
	if err != nil {
		return err
	}
	for i := range story.Places {
		if story.Places[i].Code == code {
			story.Places = append(story.Places[:i], story.Places[i+1:]...)
			break
		}
	}
	return s.updateStory(ctx, id, story)
}

func (s *ScenarioService) getStory(ctx context.Context, id int) (*storytelling.Story, error) {
	storyString, err := s.scenariosRepo.GetStoryByScenarioID(ctx, id)
	if err != nil {
		return nil, err
	}
	return mapStory(storyString, s.domain)
}

func (s *ScenarioService) updateStory(ctx context.Context, id int, story *storytelling.Story) error {
	mapCodes := map[string]struct{}{}
	for _, place := range story.Places {
		mapCodes[place.Code] = struct{}{}
		place.Image = strings.TrimPrefix(place.Image, s.domain)
	}
	if len(mapCodes) != len(story.Places) {
		return errors.New("Такой код точки уже существует")
	}

	cleanPlaces := make([]*storytelling.Place, 0, len(story.Places))
	for _, place := range story.Places {
		if place.Code == "" {
			continue
		}
		cleanPlaces = append(cleanPlaces, place)
	}
	story.Places = cleanPlaces

	storyString, err := convertStory(story)
	if err != nil {
		return err
	}

	return s.scenariosRepo.UpdateStoryByScenarioID(ctx, id, storyString)
}
