// model/period.go
package model

import (
	"database/sql"
	"fmt"
	"time"
)

// PeriodType defines the granularity of the revenue entry
type PeriodType string

const (
	PeriodQuarter  PeriodType = "Q"
	PeriodHalfYear PeriodType = "H"
	PeriodYear     PeriodType = "Y"
)

// Period holds the actual time range for a revenue entry
type Period struct {
	ID    int
	Type  PeriodType
	Year  int
	Index int // Q1=1 Q2=2 Q3=3 Q4=4 | H1=1 H2=2 | FY=1
	Start time.Time
	End   time.Time
}

func (p *Period) Validate(checkID bool) error {
	if p.ID == 0 && checkID {
		return fmt.Errorf("No ID Set")
	} else if p.Type == PeriodQuarter && (p.Index > 4 || p.Index < 1) {
		return fmt.Errorf("Not Valid Quarter index")
	} else if p.Type == PeriodHalfYear && (p.Index > 2 || p.Index < 1) {
		return fmt.Errorf("Not Valid HalfYear index")
	} else if p.Type == PeriodHalfYear && (p.Index != 1 || p.Year < 1) {
		return fmt.Errorf("Not Valid Year index")
	}
	return nil
}

func (p *Period) Insert(db *sql.DB) error {
	_, err := db.Exec(
		`INSERT INTO periods (type, year, idx, start_date, end_date) VALUES (?, ?, ?, ?, ?)
         ON CONFLICT(type, year, idx) DO UPDATE SET start_date=excluded.start_date, end_date=excluded.end_date`,
		string(p.Type), p.Year, p.Index, p.Start.Format("2006-01-02"), p.End.Format("2006-01-02"),
	)
	if err != nil {
		return fmt.Errorf("upsert period: %w", err)
	}

	var id int
	err = db.QueryRow(
		`SELECT id FROM periods WHERE type = ? AND year = ? AND idx = ?`,
		string(p.Type), p.Year, p.Index,
	).Scan(&id)
	if err != nil {
		return fmt.Errorf("select period: %w", err)
	}
	p.ID = id
	return nil
}

func QuarterPeriod(year, q int) Period {
	months := map[int][2]int{
		1: {1, 3}, 2: {4, 6}, 3: {7, 9}, 4: {10, 12},
	}
	m := months[q]
	return Period{
		Type:  PeriodQuarter,
		Year:  year,
		Index: q,
		Start: date(year, m[0], 1),
		End:   date(year, m[1]+1, 0), // last day of end month
	}
}

func HalfYearPeriod(year, h int) Period {
	if h == 1 {
		return Period{Type: PeriodHalfYear, Year: year, Index: 1,
			Start: date(year, 1, 1), End: date(year, 7, 0)}
	}
	return Period{Type: PeriodHalfYear, Year: year, Index: 2,
		Start: date(year, 7, 1), End: date(year, 12, 31)}
}

func FullYearPeriod(year int) Period {
	return Period{Type: PeriodYear, Year: year, Index: 1,
		Start: date(year, 1, 1), End: date(year, 12, 31)}
}

func (p Period) String() string {
	switch p.Type {
	case PeriodQuarter:
		return fmt.Sprintf("Q%d %d", p.Index, p.Year)
	case PeriodHalfYear:
		return fmt.Sprintf("H%d %d", p.Index, p.Year)
	case PeriodYear:
		return fmt.Sprintf("FY%d", p.Year)
	}
	return "unknown"
}

func date(year, month, day int) time.Time {
	// day=0 means last day of previous month
	return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
}
