package search

import (
	"fmt"
	"strings"
	"time"

	"github.com/gosimple/slug"
	"github.com/photoprism/photoprism/internal/entity"
	"github.com/ulule/deepcopier"
)

// Photo represents a photo search result.
type Photo struct {
	ID               uint          `json:"-"`
	CompositeID      string        `json:"ID"`
	UUID             string        `json:"DocumentID,omitempty"`
	PhotoUID         string        `json:"UID"`
	PhotoType        string        `json:"Type"`
	TypeSrc          string        `json:"TypeSrc"`
	TakenAt          time.Time     `json:"TakenAt"`
	TakenAtLocal     time.Time     `json:"TakenAtLocal"`
	TakenSrc         string        `json:"TakenSrc"`
	TimeZone         string        `json:"TimeZone"`
	PhotoPath        string        `json:"Path"`
	PhotoName        string        `json:"Name"`
	OriginalName     string        `json:"OriginalName"`
	PhotoTitle       string        `json:"Title"`
	PhotoDescription string        `json:"Description"`
	PhotoYear        int           `json:"Year"`
	PhotoMonth       int           `json:"Month"`
	PhotoDay         int           `json:"Day"`
	PhotoCountry     string        `json:"Country"`
	PhotoStack       int8          `json:"Stack"`
	PhotoFavorite    bool          `json:"Favorite"`
	PhotoPrivate     bool          `json:"Private"`
	PhotoIso         int           `json:"Iso"`
	PhotoFocalLength int           `json:"FocalLength"`
	PhotoFNumber     float32       `json:"FNumber"`
	PhotoExposure    string        `json:"Exposure"`
	PhotoFaces       int           `json:"Faces,omitempty"`
	PhotoQuality     int           `json:"Quality"`
	PhotoResolution  int           `json:"Resolution"`
	PhotoColor       uint8         `json:"Color"`
	PhotoScan        bool          `json:"Scan"`
	PhotoPanorama    bool          `json:"Panorama"`
	CameraID         uint          `json:"CameraID"` // Camera
	CameraSerial     string        `json:"CameraSerial,omitempty"`
	CameraSrc        string        `json:"CameraSrc,omitempty"`
	CameraModel      string        `json:"CameraModel"`
	CameraMake       string        `json:"CameraMake"`
	LensID           uint          `json:"LensID"` // Lens
	LensModel        string        `json:"LensModel"`
	LensMake         string        `json:"LensMake"`
	PhotoAltitude    int           `json:"Altitude,omitempty"`
	PhotoLat         float32       `json:"Lat"`
	PhotoLng         float32       `json:"Lng"`
	CellID           string        `json:"CellID"` // Cell
	CellAccuracy     int           `json:"CellAccuracy,omitempty"`
	PlaceID          string        `json:"PlaceID"`
	PlaceSrc         string        `json:"PlaceSrc"`
	PlaceLabel       string        `json:"PlaceLabel"`
	PlaceCity        string        `json:"PlaceCity"`
	PlaceState       string        `json:"PlaceState"`
	PlaceCountry     string        `json:"PlaceCountry"`
	InstanceID       string        `json:"InstanceID"`
	FileID           uint          `json:"-"` // File
	FileUID          string        `json:"FileUID"`
	FileRoot         string        `json:"FileRoot"`
	FileName         string        `json:"FileName"`
	FileHash         string        `json:"Hash"`
	FileWidth        int           `json:"Width"`
	FileHeight       int           `json:"Height"`
	FilePortrait     bool          `json:"Portrait"`
	FilePrimary      bool          `json:"-"`
	FileSidecar      bool          `json:"-"`
	FileMissing      bool          `json:"-"`
	FileVideo        bool          `json:"-"`
	FileDuration     time.Duration `json:"-"`
	FileCodec        string        `json:"-"`
	FileType         string        `json:"-"`
	FileMime         string        `json:"-"`
	FileSize         int64         `json:"-"`
	FileOrientation  int           `json:"-"`
	FileProjection   string        `json:"-"`
	FileAspectRatio  float32       `json:"-"`
	FileColors       string        `json:"-"`
	FileChroma       uint8         `json:"-"`
	FileLuminance    string        `json:"-"`
	FileDiff         uint32        `json:"-"`
	Merged           bool          `json:"Merged"`
	CreatedAt        time.Time     `json:"CreatedAt"`
	UpdatedAt        time.Time     `json:"UpdatedAt"`
	EditedAt         time.Time     `json:"EditedAt,omitempty"`
	CheckedAt        time.Time     `json:"CheckedAt,omitempty"`
	DeletedAt        time.Time     `json:"DeletedAt,omitempty"`

	Files []entity.File `json:"Files"`
}

type PhotoResults []Photo

// UIDs returns a slice of photo UIDs.
func (m PhotoResults) UIDs() []string {
	result := make([]string, len(m))

	for i, el := range m {
		result[i] = el.PhotoUID
	}

	return result
}

func (m PhotoResults) Merged() (PhotoResults, int, error) {
	count := len(m)
	merged := make([]Photo, 0, count)

	var lastId uint
	var i int

	for _, res := range m {
		file := entity.File{}

		if err := deepcopier.Copy(&file).From(res); err != nil {
			return merged, count, err
		}

		file.ID = res.FileID
		res.CompositeID = fmt.Sprintf("%d-%d", res.ID, res.FileID)

		if lastId == res.ID && i > 0 {
			merged[i-1].Files = append(merged[i-1].Files, file)
			merged[i-1].Merged = true
			continue
		}

		res.Files = append(res.Files, file)

		merged = append(merged, res)

		lastId = res.ID
		i++
	}

	return merged, count, nil
}

// ShareBase returns a meaningful file name for sharing.
func (m *Photo) ShareBase(seq int) string {
	var name string

	if m.PhotoTitle != "" {
		name = strings.Title(slug.MakeLang(m.PhotoTitle, "en"))
	} else {
		name = m.PhotoUID
	}

	taken := m.TakenAtLocal.Format("20060102-150405")

	if seq > 0 {
		return fmt.Sprintf("%s-%s (%d).%s", taken, name, seq, m.FileType)
	}

	return fmt.Sprintf("%s-%s.%s", taken, name, m.FileType)
}
