package cif

import (
	"sort"
	"time"

	"git.fjla.uk/owlboard/timetable-mgr/dbAccess"
	"git.fjla.uk/owlboard/timetable-mgr/helpers"
	"git.fjla.uk/owlboard/timetable-mgr/log"
	"git.fjla.uk/owlboard/timetable-mgr/nrod"
	"go.uber.org/zap"
)

// Runs a full update of the CIF Data, discarding any existing data and returns a new metadata struct
func runFullUpdate(cfg *helpers.Configuration) (*dbAccess.CifMetadata, error) {
	log.Msg.Warn("All existing timetable data will be deleted")
	url, err := getUpdateUrl("full")
	if err != nil {
		log.Msg.Error("Unable to get update URL", zap.Error(err))
		return nil, err
	}

	fullCifData, err := nrod.NrodDownload(url, cfg)
	if err != nil {
		log.Msg.Error("Unable to get CIF Data", zap.Error(err))
		return nil, err
	}

	log.Msg.Debug("CIF Data Downloaded", zap.ByteString("CIF Data", fullCifData))

	// I now need to define a processing function and ensure a valid type exists, then I can pass that type to a CIF Put Full function
	// which will handle placing the data into the database

	return nil, nil
}

// Runs the daily update for CIF Data, can handle up to five days updates at once.
func runUpdate(metadata *dbAccess.CifMetadata, cfg *helpers.Configuration) (*dbAccess.CifMetadata, error) {
	// Do not run update if last update was on same day
	if isSameToday(metadata.LastUpdate) {
		log.Msg.Info("No CIF Update Required", zap.Time("Last update", metadata.LastUpdate))
		return nil, nil
	}

	// Do not run update before 0600 as todays data will not be available
	if time.Now().Hour() < 6 {
		log.Msg.Info("Too early to update CIF Data")
		return nil, nil
	}

	// Check how many days ago last update was
	lastUpateDays := howManyDaysAgo(metadata.LastUpdate)
	if lastUpateDays > 5 {
		log.Msg.Warn("CIF Data is more than five days old.  Running Full Update")
		newMeta, err := runFullUpdate(cfg)
		if err != nil {
			log.Msg.Error("CIF Update failed", zap.Error(err))
			return nil, err
		}
		return newMeta, nil
	}

	// Create a slice containing which dates need updating
	firstUpdate := time.Now().In(time.UTC).AddDate(0, 0, -lastUpateDays)
	finalUpdate := time.Now().In(time.UTC)
	var dates []time.Time

	for d := firstUpdate; d.Before(finalUpdate) || d.Equal(finalUpdate); d = d.AddDate(0, 0, 1) {
		dates = append(dates, d)
	}

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

	log.Msg.Info("Updating CIF Data", zap.Any("dates to update", dates))

	// Iterate over each date, fetching then parsing the data
	for _, date := range dates {
		data, err := fetchUpdate(date, cfg)
		if err != nil {
			log.Msg.Error("Error fetching data", zap.Time("date", date))
			continue
		} // parseCifData function needs writing
		parsedData, err := parseCifData(data, metadata)
		if err != nil {
			log.Msg.Error("Error parsing data", zap.Time("date", date))
		}
		// Apply data to Database
		log.Msg.Info("CIF Data updated", zap.Time("date", date))
	}

	// Use the values in metadata to determine which day to attempt to update.
	// First check if the last update was today, if so, I can return nil, nil - No update required
	////// If the update was on the previous day, download todays data and check the sequence number and timestamp indicate that todays data is the next file that I need.
	////// If the sequence number and timestamp indicate I have missed a day, download that days data first, then todays.

	// Write a parsing function that can handle VSTP as well as SCHEDULE data
	// Handle database management
}

// Fetches CIF Updates for a given day
func fetchUpdate(t time.Time, cfg *helpers.Configuration) ([]byte, error) {
	url, err := getUpdateUrl("daily")
	if err != nil {
		return nil, err
	}

	url = url + getDayString(t)

	downloadedData, err := nrod.NrodDownload(url, cfg)
	if err != nil {
		return nil, err
	}
	return downloadedData, nil
}
