package tg

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
	"stud.l9labs.ru/bot/modules/api"
	"stud.l9labs.ru/bot/modules/database"
	"stud.l9labs.ru/bot/modules/ssauparser"
	"xorm.io/xorm"
)

// –ü–æ–ª—É—á–µ–Ω–∏–µ —Ä–∞—Å–ø–∏—Å–∞–Ω–∏—è –∏–∑ –∫–æ–º–∞–Ω–¥—ã /{group, staff} ID_ —Ä–∞—Å–ø–∏—Å–∞–Ω–∏—è
func (bot *Bot) GetSheduleFromCmd(
	now time.Time,
	user *database.TgUser,
	query string,
) (
	tgbotapi.Message,
	error,
) {
	isGroup := strings.Contains(query, "/group")
	cmd := strings.Split(query, " ")
	if len(cmd) == 1 {
		return bot.SendMsg(user, "–ù–µ–æ–±—Ö–æ–¥–∏–º–æ —É–∫–∞–∑–∞—Ç—å ID —Ä–∞—Å–ø–∏—Å–∞–Ω–∏—è",
			nilKey)
	}
	sheduleID, err := strconv.ParseInt(cmd[1], 10, 64)
	if err != nil {
		return bot.SendMsg(user, "–ù–µ–∫–æ—Ä—Ä–µ–∫—Ç–Ω—ã–π ID —Ä–∞—Å–ø–∏—Å–∞–Ω–∏—è",
			nilKey)
	}
	shedule := ssauparser.WeekShedule{
		IsGroup:   isGroup,
		SheduleID: sheduleID,
	}
	notExists, _ := ssauparser.CheckGroupOrTeacher(bot.DB, shedule)

	return bot.ReturnSummary(notExists, user, shedule, now)
}

// –°–æ–∑–¥–∞–Ω–∏—è —Å–æ–æ–±—â–µ–Ω–∏—è —Å —Ä–∞—Å–ø–∏—Å–∞–Ω–∏–µ–º —Å–µ—Å—Å–∏–∏
func (bot *Bot) GetSession(
	shedule database.Schedule,
	editMsg ...tgbotapi.Message,
) (
	tgbotapi.Message,
	error,
) {
	if _, err := bot.ActShedule(&shedule); err != nil {
		return nilMsg, err
	}
	query := "GroupId = ?"
	if !shedule.IsGroup {
		query = "TeacherId = ?"
	}
	var lessons []database.Lesson
	if err := bot.DB.
		In("Type", database.Consult, database.Exam).
		Where(query, shedule.ScheduleID).
		Asc("Begin").
		Find(&lessons); err != nil {
		return nilMsg, err
	}
	str := "<b>–†–∞—Å–ø–∏—Å–∞–Ω–∏–µ —Å–µ—Å—Å–∏–∏:</b>\n\n"
	if len(lessons) == 0 {
		str = "–†–∞—Å–ø–∏—Å–∞–Ω–∏—è —Å–µ—Å—Å–∏–∏ —Ç—É—Ç –ø–æ–∫–∞ –Ω–µ—Ç\n"
		if shedule.IsPersonal {
			str += "–ö–∞–∫ —Ç–æ–ª—å–∫–æ –æ–Ω–æ –ø–æ—è–≤–∏—Ç—Å—è, —è –æ–±—è–∑–∞—Ç–µ–ª—å–Ω–æ —Å–æ–æ–±—â—É!"
		}

		markup := SummaryKeyboard(
			Week,
			shedule,
			0,
			false,
		)

		return bot.EditOrSend(shedule.TgUser.TgId, str, "", markup, editMsg...)
	}

	for i, l := range lessons {
		if i > 0 &&
			lessons[i-1].Name == l.Name &&
			lessons[i-1].Type == l.Type {
			continue
		}
		obj := fmt.Sprintf(
			l.Begin.Format("üìÜ <b>02 %s (%s) 15:04</b>\n"),
			Month[l.Begin.Month()-1],
			weekdaysNom[int(l.Begin.Weekday())],
		)
		obj += fmt.Sprintf("<i>%s</i>\n%s %s\n",
			l.Name, Icons[l.Type], Comm[l.Type],
		)
		if l.Place != "" {
			obj += fmt.Sprintf("üß≠ %s\n", l.Place)
		}
		if l.TeacherId != 0 {
			staff, err := api.GetStaff(bot.DB, l.TeacherId)
			if err != nil {
				return nilMsg, err
			}
			obj += fmt.Sprintf("üë§ %s %s\n", staff.FirstName, staff.ShortName)
		}
		if !shedule.IsGroup {
			group, err := api.GetGroup(bot.DB, l.GroupId)
			if err != nil {
				return nilMsg, err
			}
			obj += fmt.Sprintf("üë• %s\n", group.GroupName)
		}
		obj += "------------------------------------------\n"

		str += obj
	}

	return bot.SendMsg(shedule.TgUser, str, nilKey)
}

func (bot *Bot) GetPersonal(
	now time.Time,
	shedule database.Schedule,
	editMsg ...tgbotapi.Message,
) (
	tgbotapi.Message,
	error,
) {
	exists, err := bot.ActShedule(&shedule)
	if err != nil {
		return nilMsg, err
	}

	if !exists {
		return bot.SendMsg(
			shedule.TgUser,
			"–£ —Ç–µ–±—è –ø–æ–∫–∞ –Ω–∏–∫–∞–∫–æ–≥–æ —Ä–∞—Å–ø–∏—Å–∞–Ω–∏—è –Ω–µ –ø–æ–¥–∫–ª—é—á–µ–Ω–æ\n\n"+
				"–í–≤–µ–¥–∏ <b>–Ω–æ–º–µ—Ä –≥—Ä—É–ø–ø—ã</b> "+
				"(–≤ —Ñ–æ—Ä–º–∞—Ç–µ 2305 –∏–ª–∏ 2305-240502D), "+
				"–∏ –≤ –ø–æ—è–≤–∏–≤—à–µ–º—Å—è —Ä–∞—Å–ø–∏—Å–∞–Ω–∏–∏ –Ω–∞–∂–º–∏ <b>üîî –ü–æ–¥–∫–ª—é—á–∏—Ç—å —É–≤–µ–¥–æ–º–ª–µ–Ω–∏—è</b>\n\n"+
				"https://youtube.com/shorts/FHE2YAGYBa8",
			tgbotapi.ReplyKeyboardRemove{RemoveKeyboard: true},
		)
	}

	return bot.GetWeekSummary(now, shedule, -1, "", editMsg...)

}

// –ê–∫—Ç—É–∞–ª–∏–∑–∞—Ü–∏—è –∑–∞–ø—Ä–æ—Å–∞ –Ω–∞ —Ä–∞—Å–ø–∏—Å–∞–Ω–∏–µ –¥–ª—è –ø–µ—Ä—Å–æ–Ω–∞–ª—å–Ω—ã—Ö —Ä–∞—Å–ø–∏—Å–∞–Ω–∏–π
func (bot *Bot) ActShedule(schedule *database.Schedule) (bool, error) {
	var sh database.ShedulesInUser
	var exists bool
	var err error
	if schedule.IsPersonal {
		exists, err = bot.DB.Where("L9Id = ?", schedule.TgUser.L9Id).Get(&sh)
		if err != nil {
			return false, err
		}
		schedule.IsGroup = sh.IsGroup
		schedule.ScheduleID = sh.SheduleId
	}

	return exists, nil
}

// –ü–æ–ª—É—á–∏—Ç—å —Ä–∞—Å–ø–∏—Å–∞–Ω–∏–µ –Ω–∞ –¥–µ–Ω—å
func (bot *Bot) GetDaySummary(
	now time.Time,
	schedule database.Schedule,
	dt int,
	editMsg ...tgbotapi.Message,
) (
	tgbotapi.Message,
	error,
) {
	day := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
	day = day.AddDate(0, 0, dt)
	if _, err := bot.ActShedule(&schedule); err != nil {
		return nilMsg, err
	}
	lessons, err := api.GetDayLessons(bot.DB, schedule, day)
	if err != nil {
		return nilMsg, err
	}
	dayStr := DayStr(day)
	connectButton := !schedule.IsPersonal && !bot.IsThereUserShedule(schedule.TgUser)
	markup := SummaryKeyboard(Day, schedule, dt, connectButton)

	if len(lessons) != 0 {
		pairs := api.GroupPairs(lessons)
		var str string
		firstPair := pairs[0][0].Begin

		if firstPair.Day() != day.Day() {
			str = fmt.Sprintf("–í %s, –∑–∞–Ω—è—Ç–∏–π –Ω–µ—Ç", dayStr)

			return bot.EditOrSend(schedule.TgUser.TgId, str, "", markup, editMsg...)
		}
		str = fmt.Sprintf("–†–∞—Å–ø–∏—Å–∞–Ω–∏–µ –Ω–∞ %s\n\n", dayStr)

		// TODO: –ø—Ä–∏–¥—É–º–∞—Ç—å —Å–∫–∞—á–∫–∏ –¥–ª—è –ø—É—Å—Ç—ã—Ö –¥–Ω–µ–π
		day, err := bot.StrDayShedule(pairs, schedule.IsGroup)
		if err != nil {
			return nilMsg, err
		}
		str += day

		return bot.EditOrSend(schedule.TgUser.TgId, str, "", markup, editMsg...)
	}
	str := fmt.Sprintf("–í %s, –∑–∞–Ω—è—Ç–∏–π –Ω–µ—Ç", dayStr)

	return bot.EditOrSend(schedule.TgUser.TgId, str, "", markup, editMsg...)
	//return bot.SendMsg(schedule.TgUser, "–û–π! –ü–∞—Ä –Ω–µ –æ–±–Ω–∞—Ä—É–∂–µ–Ω–æ ):", nil)
}

// –°—Ç—Ä–æ–∫–∞ –¥–∞—Ç—ã —Ñ–æ—Ä–º–∞—Ç–∞ "—Å—Ä–µ–¥—É, 1 —è–Ω–≤–∞—Ä—è"
func DayStr(day time.Time) string {
	dayStr := fmt.Sprintf(
		"%s, <b>%d %s</b>",
		weekdays[int(day.Weekday())],
		day.Day(),
		Month[day.Month()-1],
	)

	return dayStr
}

// –ó–∞–≥—Ä—É–∑–∫–∞ —Ä–∞—Å–ø–∏—Å–∞–Ω–∏—è –∏–∑ ssau.ru/rasp
func (bot *Bot) LoadShedule(shedule ssauparser.WeekShedule, now time.Time, fast bool) (
	[]database.Lesson,
	[]database.Lesson,
	error,
) {
	sh := ssauparser.WeekShedule{
		SheduleID: shedule.SheduleID,
		IsGroup:   shedule.IsGroup,
	}
	var start, end int
	if fast {
		_, start = now.ISOWeek()
		start -= bot.Week
		end = start + 1
	} else {
		start = 1
		end = 25
	}
	var add, del []database.Lesson
	for week := start; week < end; week++ {
		sh.Week = week
		if err := sh.DownloadByID(true); err != nil {
			if strings.Contains(err.Error(), "404") {
				break
			}

			return nil, nil, err
		}
		a, d, err := ssauparser.UpdateSchedule(bot.DB, sh)
		if err != nil {
			return nil, nil, err
		}
		add = append(add, a...)
		del = append(del, d...)
	}
	// –û–±–Ω–æ–≤–ª—è–µ–º –≤—Ä–µ–º—è –æ–±–Ω–æ–≤–ª–µ–Ω–∏—è
	if len(add) > 0 || len(del) > 0 {
		if sh.IsGroup {
			group, err := api.GetGroup(bot.DB, sh.SheduleID)
			if err != nil {
				return nil, nil, err
			}
			group.LastUpd = now
			if err := api.UpdateGroup(bot.DB, group); err != nil {
				return nil, nil, err
			}
		} else {
			staff, err := api.GetStaff(bot.DB, sh.SheduleID)
			if err != nil {
				return nil, nil, err
			}
			staff.LastUpd = now
			if err := api.UpdateStaff(bot.DB, staff); err != nil {
				return nil, nil, err
			}
		}
	}

	return add, del, nil
}

var Icons = map[database.Kind]string{
	database.Lection:    "üìó",
	database.Practice:   "üìï",
	database.Lab:        "üìò",
	database.Other:      "üìô",
	database.Military:   "ü´°",
	database.Window:     "üèù",
	database.Exam:       "üíÄ",
	database.Consult:    "üó®",
	database.CourseWork: "ü§Ø",
	database.Test:       "üìù",
	database.Unknown:    "‚ùì",
}

var Comm = map[database.Kind]string{
	database.Lection:    "–õ–µ–∫—Ü–∏—è",
	database.Practice:   "–ü—Ä–∞–∫—Ç–∏–∫–∞",
	database.Lab:        "–õ–∞–±–∞",
	database.Other:      "–ü—Ä–æ—á–µ–µ",
	database.Military:   "",
	database.Window:     "",
	database.Exam:       "–≠–∫–∑–∞–º–µ–Ω",
	database.Consult:    "–ö–æ–Ω—Å—É–ª—å—Ç–∞—Ü–∏—è",
	database.CourseWork: "–ö—É—Ä—Å–æ–≤–∞—è",
	database.Test:       "–ó–∞—á—ë—Ç",
	database.Unknown:    "",
}

// –ö–æ–Ω–≤–µ—Ä—Ç–∞—Ü–∏—è –∑–∞–Ω—è—Ç–∏–π —Å —Ç–µ–∫—Å—Ç
func PairToStr(pair []database.Lesson, db *xorm.Engine, isGroup bool) (string, error) {
	var str string
	if len(pair) == 0 {
		return "", fmt.Errorf("empty pair")
	}
	beginStr := pair[0].Begin.Format("15:04")
	endStr := pair[0].End.Format("15:04")
	if pair[0].Type == database.Military {
		endStr = "‚àû"
	}
	str = fmt.Sprintf("üìÜ %s - %s\n", beginStr, endStr)

	var groups []database.Lesson
	if !isGroup {
		groups = pair[:]
		pair = pair[:1]
	}

	for i, sublesson := range pair {
		typeEmoji := Icons[sublesson.Type] + " " + Comm[sublesson.Type]
		str += fmt.Sprintf("%s %s\n", typeEmoji, sublesson.Name)
		if sublesson.Place != "" {
			str += fmt.Sprintf("üß≠ %s\n", sublesson.Place)
		}
		if !isGroup {
			break
		}
		if sublesson.TeacherId != 0 {
			staff, err := api.GetStaff(db, sublesson.TeacherId)
			if err != nil {
				return "", err
			}
			str += fmt.Sprintf("üë§ %s %s\n", staff.FirstName, staff.ShortName)
		}
		if sublesson.SubGroup != 0 {
			str += fmt.Sprintf("üë• –ü–æ–¥–≥—Ä—É–ø–ø–∞: %d\n", sublesson.SubGroup)
		}
		if sublesson.Comment != "" {
			str += fmt.Sprintf("üí¨ %s\n", sublesson.Comment)
		}
		if i != len(pair)-1 {
			str += "+\n"
		}
	}

	if !isGroup {
		for _, gr := range groups {
			group, err := api.GetGroup(db, gr.GroupId)
			if err != nil {
				return "", err
			}
			str += fmt.Sprintf("üë• %s\n", group.GroupName)
			if gr.SubGroup != 0 {
				str += fmt.Sprintf("üë• –ü–æ–¥–≥—Ä—É–ø–ø–∞: %d\n", gr.SubGroup)
			}
		}
		if pair[0].Comment != "" {
			str += fmt.Sprintf("üí¨ %s\n", pair[0].Comment)
		}
	}

	str += "------------------------------------------\n"

	return str, nil
}

// –¢–µ–∫—Å—Ç —Ä–∞—Å–ø–∏—Å–∞–Ω–∏—è –Ω–∞ –¥–µ–Ω—å
func (bot *Bot) StrDayShedule(lessons [][]database.Lesson, isGroup bool) (string, error) {
	var str string
	day := lessons[0][0].Begin.Day()
	for _, pair := range lessons {
		if pair[0].Begin.Day() == day {
			line, err := PairToStr(pair, bot.DB, isGroup)
			if err != nil {
				return "", err
			}
			str += line
		} else {
			break
		}
	}

	return str, nil
}
