package schedule_parser

import (
	"context"
	"log"
	"pinned_message/internal/models"
	"pinned_message/internal/modules/data_parser"
	"pinned_message/internal/modules/date_parser"
	"pinned_message/internal/services/schedule_storage"
	"sort"
	"strings"
	"time"
)

type performance struct {
	Date           string `csv:"ДАТА"`
	Day            string `csv:"день недели"`
	Name           string `csv:"НАЗВАНИЕ"`
	Place          string `csv:"МЕСТО"`
	TimeCollection string `csv:"время СБОРА (для концерта)"`
	TimeStart      string `csv:"время НАЧАЛА"`
	Numbers        string `csv:"ЧТО ТАНЦУЕМ"`
	Costumes       string `csv:"КОСТЮМЫ"`
}

type ScheduleParser struct {
	dataParser      data_parser.IDataParser
	dateParser      date_parser.IDateParser
	scheduleStorage *schedule_storage.ScheduleStorage
}

func NewScheduleParser(
	dataParser data_parser.IDataParser,
	dateParser date_parser.IDateParser,
	scheduleStorage *schedule_storage.ScheduleStorage,
) *ScheduleParser {
	return &ScheduleParser{
		dataParser:      dataParser,
		dateParser:      dateParser,
		scheduleStorage: scheduleStorage,
	}
}

func (p *ScheduleParser) Run(ctx context.Context) {
	ticker := time.NewTicker(30 * time.Minute)
	defer ticker.Stop()

	sheetURL := "https://docs.google.com/spreadsheets/d/1v57bCAG764j1ULXDMb3amNFMzkkLmObKWsl5oE0Xq00/edit?gid=57461713#gid=57461713"
	p.parseAndSaveSchedule(ctx, sheetURL)
	for {
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
			p.parseAndSaveSchedule(ctx, sheetURL)
		}
	}
}

func (p *ScheduleParser) parseAndSaveSchedule(ctx context.Context, sheetURL string) {
	schedule, err := p.parseSchedule(ctx, sheetURL)
	if err != nil {
		log.Printf("Error parse schedule: %s\n", sheetURL)
		return
	}
	if err := p.scheduleStorage.SaveSchedule(schedule); err != nil {
		log.Printf("Error save err: %s schedule: %s\n", err, sheetURL)
	}
}

func (p *ScheduleParser) parseSchedule(ctx context.Context, sheetURL string) (*models.Schedule, error) {
	var performances []performance
	if err := p.dataParser.Parse(ctx, sheetURL, &performances); err != nil {
		return nil, err
	}
	return p.mapSchedule(performances), nil
}

func (p *ScheduleParser) mapSchedule(performances []performance) *models.Schedule {
	days := map[time.Time]*models.Day{}
	var currentDate time.Time
	var prevDate time.Time
	for _, performance := range performances {
		if performance.Name == "" || performance.Name == "-" {
			continue
		}

		if performance.Date != "" {
			date, err := p.mapDate(performance.Date)
			if err != nil {
				panic(err)
			}
			currentDate = date
		}
		if performance.Date == "" {
			currentDate = prevDate
		}

		if _, ok := days[currentDate]; !ok {
			days[currentDate] = &models.Day{
				Date: currentDate,
			}
		}
		currentDay := days[currentDate]

		currentDay.Performances = append(
			currentDay.Performances,
			&models.DayPerformance{
				TimeCollection: performance.TimeCollection,
				TimeStart:      performance.TimeStart,
				Place:          performance.Place,
				Name:           performance.Name,
				Numbers:        p.mapNumbers(performance.Numbers),
				Costumes:       performance.Costumes,
			},
		)
		prevDate = currentDate
	}

	return &models.Schedule{
		UpdateTime: time.Now().String(),
		Days:       p.mapToSortedArray(days),
	}
}

func (p *ScheduleParser) mapDate(date string) (time.Time, error) {
	if date == "" {
		return time.Time{}, nil
	}
	return p.dateParser.Parse(date)
}

func (p *ScheduleParser) mapNumbers(numbers string) []*models.Number {
	// вкусовщина и костыли
	numbers = strings.ReplaceAll(numbers, "Ай, да русская душа", "Ай да русская душа")
	numbers = strings.ReplaceAll(numbers, "«", "\"")
	numbers = strings.ReplaceAll(numbers, "»", "\"")
	numbers = strings.ReplaceAll(numbers, "”", "\"")

	names := strings.Split(numbers, ",")
	res := make([]*models.Number, 0, len(names))
	for _, name := range names {
		res = append(
			res,
			&models.Number{
				Name: name,
			},
		)
	}
	return res
}

func (p *ScheduleParser) mapToSortedArray(m map[time.Time]*models.Day) []*models.Day {
	result := make([]*models.Day, 0, len(m))

	for _, day := range m {
		result = append(result, day)
	}

	sort.Slice(result, func(i, j int) bool {
		return result[i].Date.Before(result[j].Date)
	})

	return result
}
