package resume

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"gopkg.in/yaml.v3"
)

// DatePrecision indicates what level of detail a date carries.
type DatePrecision int

const (
	PrecisionFull  DatePrecision = iota // Full date (day-level)
	PrecisionMonth                      // Month + year only
	PrecisionYear                       // Year only
)

// PartialDate is a date with explicit precision, so formatters know whether to
// display "2017", "May 2017", or the full date.
type PartialDate struct {
	Time      time.Time
	Precision DatePrecision
}

// IsZero reports whether the underlying time is zero.
func (pd PartialDate) IsZero() bool {
	return pd.Time.IsZero()
}

// After reports whether pd is after other.
func (pd PartialDate) After(other PartialDate) bool {
	return pd.Time.After(other.Time)
}

// UnmarshalYAML parses "2017", "2017-05", or a full timestamp.
func (pd *PartialDate) UnmarshalYAML(value *yaml.Node) error {
	s := strings.TrimSpace(value.Value)

	// Year-only: "2017" (may arrive as !!int or !!str)
	if len(s) == 4 {
		if t, err := time.Parse("2006", s); err == nil {
			pd.Time = t
			pd.Precision = PrecisionYear
			return nil
		}
	}

	// Month-year: "2017-05"
	if len(s) == 7 {
		if t, err := time.Parse("2006-01", s); err == nil {
			pd.Time = t
			pd.Precision = PrecisionMonth
			return nil
		}
	}

	// Full date / timestamp – delegate to yaml.v3's time parser.
	var t time.Time
	if err := value.Decode(&t); err != nil {
		return fmt.Errorf("cannot parse date %q: expected YYYY, YYYY-MM, or full date/timestamp", s)
	}
	pd.Time = t
	pd.Precision = PrecisionFull
	return nil
}

// MarshalYAML outputs the date in its precision-appropriate format.
func (pd PartialDate) MarshalYAML() (interface{}, error) {
	switch pd.Precision {
	case PrecisionYear:
		return pd.Time.Format("2006"), nil
	case PrecisionMonth:
		return pd.Time.Format("2006-01"), nil
	default:
		return pd.Time.Format(time.RFC3339), nil
	}
}

// UnmarshalJSON parses "2017", "2017-05", or an RFC 3339 timestamp.
func (pd *PartialDate) UnmarshalJSON(data []byte) error {
	s := strings.Trim(string(data), `"`)

	if len(s) == 4 {
		if t, err := time.Parse("2006", s); err == nil {
			pd.Time = t
			pd.Precision = PrecisionYear
			return nil
		}
	}
	if len(s) == 7 {
		if t, err := time.Parse("2006-01", s); err == nil {
			pd.Time = t
			pd.Precision = PrecisionMonth
			return nil
		}
	}
	if t, err := time.Parse(time.RFC3339, s); err == nil {
		pd.Time = t
		pd.Precision = PrecisionFull
		return nil
	}
	if t, err := time.Parse("2006-01-02", s); err == nil {
		pd.Time = t
		pd.Precision = PrecisionFull
		return nil
	}
	return fmt.Errorf("cannot parse date %q", s)
}

// MarshalJSON outputs the date in its precision-appropriate format.
func (pd PartialDate) MarshalJSON() ([]byte, error) {
	switch pd.Precision {
	case PrecisionYear:
		return json.Marshal(pd.Time.Format("2006"))
	case PrecisionMonth:
		return json.Marshal(pd.Time.Format("2006-01"))
	default:
		return json.Marshal(pd.Time.Format(time.RFC3339))
	}
}

// NewPartialDate creates a PartialDate from a time.Time with the given precision.
func NewPartialDate(t time.Time, p DatePrecision) PartialDate {
	return PartialDate{Time: t, Precision: p}
}

// NewMonthDate is a convenience for creating a month-precision date (the most common case).
func NewMonthDate(t time.Time) PartialDate {
	return PartialDate{Time: t, Precision: PrecisionMonth}
}

// NewYearDate is a convenience for creating a year-precision date.
func NewYearDate(t time.Time) PartialDate {
	return PartialDate{Time: t, Precision: PrecisionYear}
}

type Resume struct {
	Contact        Contact         `json:"contact" yaml:"contact" toml:"contact"`
	Summary        string          `json:"summary,omitempty" yaml:"summary,omitempty" toml:"summary,omitempty"`
	Certifications *Certifications `json:"certifications,omitempty" yaml:"certifications,omitempty" toml:"certifications,omitempty"`
	Skills         Skills          `json:"skills" yaml:"skills" toml:"skills"`
	Experience     ExperienceList  `json:"experience" yaml:"experience" toml:"experience"`
	Projects       *ProjectList    `json:"projects,omitempty" yaml:"projects,omitempty" toml:"projects,omitempty"`
	Education      EducationList   `json:"education" yaml:"education" toml:"education"`
	Languages      *LanguageList   `json:"languages,omitempty" yaml:"languages,omitempty" toml:"languages,omitempty"`
	Layout         *Layout         `json:"layout,omitempty" yaml:"layout,omitempty" toml:"layout,omitempty"`
}

type Layout struct {
	Density      string    `json:"density,omitempty" yaml:"density,omitempty" toml:"density,omitempty"`
	Typography   string    `json:"typography,omitempty" yaml:"typography,omitempty" toml:"typography,omitempty"`
	Header       string    `json:"header,omitempty" yaml:"header,omitempty" toml:"header,omitempty"`
	SkillColumns int       `json:"skill_columns,omitempty" yaml:"skill_columns,omitempty" toml:"skill_columns,omitempty"`
	References   bool      `json:"references,omitempty" yaml:"references,omitempty" toml:"references,omitempty"`
	Emphasis     *Emphasis `json:"emphasis,omitempty" yaml:"emphasis,omitempty" toml:"emphasis,omitempty"`
}

// Emphasis controls bolding of quantified facts and key phrases in the summary
// and highlight bullets so they stand out when a reader skims the resume.
type Emphasis struct {
	// Metrics bolds numbers, dollar amounts, percentages, and multipliers
	// (e.g. "$2M", "1,500", "45-55%", "4x", "2B", "200+").
	Metrics bool `json:"metrics,omitempty" yaml:"metrics,omitempty" toml:"metrics,omitempty"`
	// Phrases bolds each listed phrase wherever it appears, matched
	// case-insensitively.
	Phrases []string `json:"phrases,omitempty" yaml:"phrases,omitempty" toml:"phrases,omitempty"`
}

type LanguageList struct {
	Title     string     `json:"title,omitempty" yaml:"title,omitempty" toml:"title,omitempty"`
	Languages []Language `json:"languages" yaml:"languages" toml:"languages"`
}

type Language struct {
	Name        string `json:"name" yaml:"name" toml:"name"`
	Proficiency string `json:"proficiency,omitempty" yaml:"proficiency,omitempty" toml:"proficiency,omitempty"`
}

type Certifications struct {
	Title string          `json:"title,omitempty" yaml:"title,omitempty" toml:"title,omitempty"`
	Items []Certification `json:"items" yaml:"items" toml:"items"`
}

type Certification struct {
	Name   string       `json:"name" yaml:"name" toml:"name"`
	Issuer string       `json:"issuer,omitempty" yaml:"issuer,omitempty" toml:"issuer,omitempty"`
	Notes  string       `json:"notes,omitempty" yaml:"notes,omitempty" toml:"notes,omitempty"`
	Date   *PartialDate `json:"date,omitempty" yaml:"date,omitempty" toml:"date,omitempty"`
}

type Location struct {
	City     string `json:"city" yaml:"city" toml:"city"`
	State    string `json:"state,omitempty" yaml:"state,omitempty" toml:"state,omitempty"`
	Province string `json:"province,omitempty" yaml:"province,omitempty" toml:"province,omitempty"`
	Country  string `json:"country,omitempty" yaml:"country,omitempty" toml:"country,omitempty"`
	Remote   bool   `json:"remote,omitempty" yaml:"remote,omitempty" toml:"remote,omitempty"`
}

type Link struct {
	URI   string `json:"uri" yaml:"uri" toml:"uri"`
	Label string `json:"label,omitempty" yaml:"label,omitempty" toml:"label,omitempty"`
}

type Contact struct {
	Name        string    `json:"name" yaml:"name" toml:"name"`
	Headline    string    `json:"headline,omitempty" yaml:"headline,omitempty" toml:"headline,omitempty"`
	Email       string    `json:"email" yaml:"email" toml:"email"`
	Phone       string    `json:"phone,omitempty" yaml:"phone,omitempty" toml:"phone,omitempty"`
	Credentials string    `json:"credentials,omitempty" yaml:"credentials,omitempty" toml:"credentials,omitempty"`
	Location    *Location `json:"location,omitempty" yaml:"location,omitempty" toml:"location,omitempty"`
	Links       []Link    `json:"links,omitempty" yaml:"links,omitempty" toml:"links,omitempty"`
}

type Skills struct {
	Title      string          `json:"title,omitempty" yaml:"title,omitempty" toml:"title,omitempty"`
	Categories []SkillCategory `json:"categories" yaml:"categories" toml:"categories"`
}

type SkillCategory struct {
	Category string   `json:"category" yaml:"category" toml:"category"`
	Items    []string `json:"items" yaml:"items" toml:"items"`
}

type ExperienceList struct {
	Title     string       `json:"title,omitempty" yaml:"title,omitempty" toml:"title,omitempty"`
	Positions []Experience `json:"positions" yaml:"positions" toml:"positions"`
}

type Experience struct {
	Company        string    `json:"company" yaml:"company" toml:"company"`
	Title          string    `json:"title" yaml:"title" toml:"title"`
	EmploymentType string    `json:"employment_type,omitempty" yaml:"employment_type,omitempty" toml:"employment_type,omitempty"`
	Highlights     []string  `json:"highlights,omitempty" yaml:"highlights,omitempty" toml:"highlights,omitempty"`
	Duties         []string  `json:"duties,omitempty" yaml:"duties,omitempty" toml:"duties,omitempty"`
	Notes          string    `json:"notes,omitempty" yaml:"notes,omitempty" toml:"notes,omitempty"`
	Dates          DateRange `json:"dates" yaml:"dates" toml:"dates"`
	Location       *Location `json:"location,omitempty" yaml:"location,omitempty" toml:"location,omitempty"`
	Technologies   []string  `json:"technologies,omitempty" yaml:"technologies,omitempty" toml:"technologies,omitempty"`
}
type ExperienceGroup struct {
	Name      string `json:"name" yaml:"name" toml:"name"`
	Positions []int  `json:"positions" yaml:"positions" toml:"positions"` // References to position orders
}

type DateRange struct {
	Start PartialDate  `json:"start" yaml:"start" toml:"start"`
	End   *PartialDate `json:"end,omitempty" yaml:"end,omitempty" toml:"end,omitempty"`
}

type ProjectList struct {
	Title    string    `json:"title,omitempty" yaml:"title,omitempty" toml:"title,omitempty"`
	Projects []Project `json:"projects" yaml:"projects" toml:"projects"`
}
type Project struct {
	Name         string     `json:"name" yaml:"name" toml:"name"`
	Link         Link       `json:"link,omitempty" yaml:"link,omitempty" toml:"link,omitempty"`
	Highlights   []string   `json:"highlights,omitempty" yaml:"highlights,omitempty" toml:"highlights,omitempty"`
	Dates        *DateRange `json:"dates,omitempty" yaml:"dates,omitempty" toml:"dates,omitempty"`
	Technologies []string   `json:"technologies,omitempty" yaml:"technologies,omitempty" toml:"technologies,omitempty"`
}

type EducationList struct {
	Title        string      `json:"title,omitempty" yaml:"title,omitempty" toml:"title,omitempty"`
	Institutions []Education `json:"institutions" yaml:"institutions" toml:"institutions"`
}

type Degree struct {
	Name         string   `json:"name" yaml:"name" toml:"name"`
	Descriptions []string `json:"descriptions,omitempty" yaml:"descriptions,omitempty" toml:"descriptions,omitempty"`
}

type GPA struct {
	GPA    string `json:"gpa,omitempty" yaml:"gpa,omitempty" toml:"gpa,omitempty"`
	MaxGPA string `json:"max_gpa,omitempty" yaml:"max_gpa,omitempty" toml:"max_gpa,omitempty"`
}

type Education struct {
	Institution     string    `json:"institution" yaml:"institution" toml:"institution"`
	Degree          Degree    `json:"degree" yaml:"degree" toml:"degree"`
	Specializations []string  `json:"specializations,omitempty" yaml:"specializations,omitempty" toml:"specializations,omitempty"`
	GPA             *GPA      `json:"gpa,omitempty" yaml:"gpa,omitempty" toml:"gpa,omitempty"`
	Awards          []Award   `json:"awards,omitempty" yaml:"awards,omitempty" toml:"awards,omitempty"`
	Dates           DateRange `json:"dates" yaml:"dates" toml:"dates"`
	Location        *Location `json:"location,omitempty" yaml:"location,omitempty" toml:"location,omitempty"`
	Thesis          *Thesis   `json:"thesis,omitempty" yaml:"thesis,omitempty" toml:"thesis,omitempty"`
}

type Thesis struct {
	Title       string   `json:"title" yaml:"title" toml:"title"`
	Highlights  []string `json:"highlights,omitempty" yaml:"highlights,omitempty" toml:"highlights,omitempty"`
	Link        Link     `json:"link" yaml:"link" toml:"link"`
	Description string   `json:"description,omitempty" yaml:"description,omitempty" toml:"description,omitempty"`
}

type Award struct {
	Name  string       `json:"name" yaml:"name" toml:"name"`
	Date  *PartialDate `json:"date,omitempty" yaml:"date,omitempty" toml:"date,omitempty"`
	Notes string       `json:"notes,omitempty" yaml:"notes,omitempty" toml:"notes,omitempty"`
}

// UnmarshalYAML implements custom YAML unmarshaling for Education to support
// "credential" as an alias for "degree".
func (e *Education) UnmarshalYAML(value *yaml.Node) error {
	// Use an alias type to avoid infinite recursion
	type educationAlias Education
	var aux educationAlias

	// Also capture the "credential" alias
	type educationRaw struct {
		educationAlias `yaml:",inline"`
		Credential     *Degree `yaml:"credential,omitempty"`
	}
	var raw educationRaw
	if err := value.Decode(&raw); err != nil {
		return err
	}
	aux = raw.educationAlias

	// If degree is empty but credential is set, use credential
	if aux.Degree.Name == "" && raw.Credential != nil {
		aux.Degree = *raw.Credential
	}

	*e = Education(aux)
	return nil
}

// UnmarshalYAML implements custom YAML unmarshaling for Degree to support
// "note" (string) as an alias for "descriptions" ([]string).
func (d *Degree) UnmarshalYAML(value *yaml.Node) error {
	// Use an alias type to avoid infinite recursion
	type degreeAlias Degree
	type degreeRaw struct {
		degreeAlias `yaml:",inline"`
		Note        string `yaml:"note,omitempty"`
	}
	var raw degreeRaw
	if err := value.Decode(&raw); err != nil {
		return err
	}

	*d = Degree(raw.degreeAlias)

	// If descriptions is empty but note is set, use note as a single-item list
	if len(d.Descriptions) == 0 && raw.Note != "" {
		d.Descriptions = []string{raw.Note}
	}

	return nil
}

func Validate(resume *Resume) []ValidationError {
	var errors []ValidationError

	// Validate contact information
	if resume.Contact.Name == "" {
		errors = append(errors, ValidationError{
			Field:   "contact.name",
			Message: "Name is required",
			Type:    "required",
		})
	}

	if resume.Contact.Email == "" {
		errors = append(errors, ValidationError{
			Field:   "contact.email",
			Message: "Email is required",
			Type:    "required",
		})
	}

	return errors
}

// ValidationError represents a configuration validation error
type ValidationError struct {
	Field   string      `json:"field"`
	Message string      `json:"message"`
	Type    string      `json:"type"`
	Value   interface{} `json:"value,omitempty"`
}
