package db

import (
	"encoding/json"
	"time"

	"skraak/utils"
)

// JSONTime wraps time.Time and marshals as RFC3339 string.
// Used as an embedded field in marshal-only structs to avoid
// duplicating MarshalJSON for every DB type that has timestamps.
type JSONTime time.Time

// MarshalJSON implements json.Marshaler for JSONTime.
func (t JSONTime) MarshalJSON() ([]byte, error) {
	return json.Marshal(time.Time(t).Format(time.RFC3339))
}

// jt converts a time.Time to JSONTime.
func jt(t time.Time) JSONTime { return JSONTime(t) }

// DatasetType represents the dataset_type enum from the schema
type DatasetType string

// Dataset type enum constants
const (
	DatasetTypeStructured   DatasetType = "structured"
	DatasetTypeUnstructured DatasetType = "unstructured"
	DatasetTypeTest         DatasetType = "test"
	DatasetTypeTrain        DatasetType = "train"
)

// Dataset represents a row from the dataset table
type Dataset struct {
	ID           string      `json:"id"`
	Name         string      `json:"name"`
	Description  *string     `json:"description"` // Pointer for nullable field
	CreatedAt    time.Time   `json:"created_at"`
	LastModified time.Time   `json:"last_modified"`
	Active       bool        `json:"active"`
	Type         DatasetType `json:"type"`
}

// MarshalJSON implements custom JSON marshaling for Dataset
func (d Dataset) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		ID           string      `json:"id"`
		Name         string      `json:"name"`
		Description  *string     `json:"description"`
		CreatedAt    JSONTime    `json:"created_at"`
		LastModified JSONTime    `json:"last_modified"`
		Active       bool        `json:"active"`
		Type         DatasetType `json:"type"`
	}{
		ID:           d.ID,
		Name:         d.Name,
		Description:  d.Description,
		CreatedAt:    jt(d.CreatedAt),
		LastModified: jt(d.LastModified),
		Active:       d.Active,
		Type:         d.Type,
	})
}

// Location represents a row from the location table
type Location struct {
	ID           string    `json:"id"`
	DatasetID    string    `json:"dataset_id"`
	Name         string    `json:"name"`
	Latitude     float64   `json:"latitude"`
	Longitude    float64   `json:"longitude"`
	Description  *string   `json:"description"` // nullable
	CreatedAt    time.Time `json:"created_at"`
	LastModified time.Time `json:"last_modified"`
	Active       bool      `json:"active"`
	TimezoneID   string    `json:"timezone_id"`
}

// MarshalJSON implements custom JSON marshaling for Location
func (l Location) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		ID           string   `json:"id"`
		DatasetID    string   `json:"dataset_id"`
		Name         string   `json:"name"`
		Latitude     float64  `json:"latitude"`
		Longitude    float64  `json:"longitude"`
		Description  *string  `json:"description"`
		CreatedAt    JSONTime `json:"created_at"`
		LastModified JSONTime `json:"last_modified"`
		Active       bool     `json:"active"`
		TimezoneID   string   `json:"timezone_id"`
	}{
		ID:           l.ID,
		DatasetID:    l.DatasetID,
		Name:         l.Name,
		Latitude:     l.Latitude,
		Longitude:    l.Longitude,
		Description:  l.Description,
		CreatedAt:    jt(l.CreatedAt),
		LastModified: jt(l.LastModified),
		Active:       l.Active,
		TimezoneID:   l.TimezoneID,
	})
}

// Cluster represents a row from the cluster table
type Cluster struct {
	ID                       string    `json:"id"`
	DatasetID                string    `json:"dataset_id"`
	LocationID               string    `json:"location_id"`
	Name                     string    `json:"name"`
	Description              *string   `json:"description"` // nullable
	CreatedAt                time.Time `json:"created_at"`
	LastModified             time.Time `json:"last_modified"`
	Active                   bool      `json:"active"`
	CyclicRecordingPatternID *string   `json:"cyclic_recording_pattern_id"` // nullable
	SampleRate               int       `json:"sample_rate"`
}

// MarshalJSON implements custom JSON marshaling for Cluster
func (c Cluster) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		ID                       string   `json:"id"`
		DatasetID                string   `json:"dataset_id"`
		LocationID               string   `json:"location_id"`
		Name                     string   `json:"name"`
		Description              *string  `json:"description"`
		CreatedAt                JSONTime `json:"created_at"`
		LastModified             JSONTime `json:"last_modified"`
		Active                   bool     `json:"active"`
		CyclicRecordingPatternID *string  `json:"cyclic_recording_pattern_id"`
		SampleRate               int      `json:"sample_rate"`
	}{
		ID:                       c.ID,
		DatasetID:                c.DatasetID,
		LocationID:               c.LocationID,
		Name:                     c.Name,
		Description:              c.Description,
		CreatedAt:                jt(c.CreatedAt),
		LastModified:             jt(c.LastModified),
		Active:                   c.Active,
		CyclicRecordingPatternID: c.CyclicRecordingPatternID,
		SampleRate:               c.SampleRate,
	})
}

// File represents a row from the file table
type File struct {
	ID              string    `json:"id"`
	FileName        string    `json:"file_name"`
	Path            *string   `json:"path"` // nullable
	XXH64Hash       string    `json:"xxh64_hash"`
	LocationID      string    `json:"location_id"`
	TimestampLocal  time.Time `json:"timestamp_local"`
	ClusterID       *string   `json:"cluster_id"` // nullable
	Duration        float64   `json:"duration"`
	SampleRate      int       `json:"sample_rate"`
	Description     *string   `json:"description"`       // nullable
	MaybeSolarNight *bool     `json:"maybe_solar_night"` // nullable
	MaybeCivilNight *bool     `json:"maybe_civil_night"` // nullable
	MoonPhase       *float64  `json:"moon_phase"`        // nullable
	CreatedAt       time.Time `json:"created_at"`
	LastModified    time.Time `json:"last_modified"`
	Active          bool      `json:"active"`
}

// CyclicRecordingPattern represents a row from the cyclic_recording_pattern table
type CyclicRecordingPattern struct {
	ID           string    `json:"id"`
	RecordS      int       `json:"record_s"`
	SleepS       int       `json:"sleep_s"`
	CreatedAt    time.Time `json:"created_at"`
	LastModified time.Time `json:"last_modified"`
	Active       bool      `json:"active"`
}

// MarshalJSON implements custom JSON marshaling for CyclicRecordingPattern
func (p CyclicRecordingPattern) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		ID           string   `json:"id"`
		RecordS      int      `json:"record_s"`
		SleepS       int      `json:"sleep_s"`
		CreatedAt    JSONTime `json:"created_at"`
		LastModified JSONTime `json:"last_modified"`
		Active       bool     `json:"active"`
	}{
		ID:           p.ID,
		RecordS:      p.RecordS,
		SleepS:       p.SleepS,
		CreatedAt:    jt(p.CreatedAt),
		LastModified: jt(p.LastModified),
		Active:       p.Active,
	})
}

// GainLevel is re-exported from utils for backward compatibility.
type GainLevel = utils.GainLevel

// Gain level constants re-exported from utils.
const (
	GainLow        = utils.GainLow
	GainLowMedium  = utils.GainLowMedium
	GainMedium     = utils.GainMedium
	GainMediumHigh = utils.GainMediumHigh
	GainHigh       = utils.GainHigh
)

// MothMetadata represents a row from the moth_metadata table
type MothMetadata struct {
	FileID       string     `json:"file_id"`
	Timestamp    time.Time  `json:"timestamp"`
	RecorderID   *string    `json:"recorder_id"` // nullable
	Gain         *GainLevel `json:"gain"`        // nullable
	BatteryV     *float64   `json:"battery_v"`   // nullable
	TempC        *float64   `json:"temp_c"`      // nullable
	CreatedAt    time.Time  `json:"created_at"`
	LastModified time.Time  `json:"last_modified"`
	Active       bool       `json:"active"`
}

// FileDataset represents a row from the file_dataset junction table
type FileDataset struct {
	FileID       string    `json:"file_id"`
	DatasetID    string    `json:"dataset_id"`
	CreatedAt    time.Time `json:"created_at"`
	LastModified time.Time `json:"last_modified"`
}