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) 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
}

type RevenueCategory struct {
	ID        int
	CompanyID int
	ParentID  *int
	Name      string // e.g. "product", "location", "segment"
}

func (rc *RevenueCategory) Validate() error {
	if rc.ID == 0 {
		return fmt.Errorf("No ID Set")
	} else if rc.Name == "" {
		return fmt.Errorf("No Name found")
	} else if rc.CompanyID == 0 {
		return fmt.Errorf("No Company Set")
	}
	return nil
}

func (rc *RevenueCategory) Insert(db *sql.DB) error {
	if err := rc.Validate(); err != nil {
		return fmt.Errorf("failed to insert: %w", err)
	}

	_, err := db.Exec(
		`INSERT INTO category (company_id, parent_id, name) VALUES (?, ?, ?)
		 ON CONFLICT(company_id, name) DO UPDATE SET parent_id=excluded.parent_id`,
		rc.CompanyID, rc.ParentID, rc.Name,
	)
	if err != nil {
		return fmt.Errorf("upsert category: %w", err)
	}

	err = db.QueryRow(
		`SELECT id FROM category WHERE company_id = ? AND name = ?`,
		rc.CompanyID, rc.Name,
	).Scan(&rc.ID)
	if err != nil {
		return fmt.Errorf("select category id: %w", err)
	}

	return nil
}

// Revenue is a single line in a financial report
type Revenue struct {
	ID       int
	Company  *Company
	Currency *Currency
	Category *RevenueCategory
	Period   *Period
	Value    float64
}
