package main

import (
	"time"

	"github.com/serverwentdown/datetime.link/data"
	"go.uber.org/zap"
)

// Zone represents any form of zone offset: this could be a city or a fixed
// offset from UTC
type Zone struct {
	// City is optional
	City *data.City
	// Offset is optional
	Offset *time.Location
}

// IsOffset is true when the Zone is an offset instead of a city
func (z Zone) IsOffset() bool {
	return z.Offset != nil
}

// Name returns the name of the zone
func (z Zone) Name() string {
	if z.IsOffset() {
		return z.Offset.String()
	} else if z.City != nil {
		return z.City.FullName()
	}
	return ""
}

// Location returns the time.Location of the zone. Useful for other functions
func (z Zone) Location() *time.Location {
	if z.IsOffset() {
		return z.Offset
	} else if z.City != nil {
		loc, err := time.LoadLocation(z.City.Timezone)
		if err != nil {
			// This is a really bad situation
			// TODO: validate this at boot time
			l.Error("unable to find timezone", zap.String("timezone", z.City.Timezone))
		}
		return loc
	}
	return nil
}

// TimeOffset returns the timezone offset at a specific time
func (z Zone) TimeOffset(t time.Time) int {
	if l := z.Location(); l != nil {
		_, offset := t.In(l).Zone()
		return offset
	}
	return -1 // TODO: better invalid handling
}

// ResolveZone resolves a zone string into a Zone
func ResolveZone(cities map[string]*data.City, zone string) (Zone, error) {
	// Try parsing as a zone offset
	offset, err := ParseZoneOffset(zone)
	if err == nil {
		offsetZone := time.FixedZone("UTC "+FormatZoneOffset(offset), offset)
		return Zone{
			Offset: offsetZone,
		}, nil
	}
	// Parse as a city
	city, err := SearchCities(cities, zone)
	if err == nil {
		return Zone{
			City: city,
		}, nil
	}
	return Zone{}, err
}
