package session

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type Data struct {
	Spotify   SpotifyState               `json:"spotify"`
	Qobuz     QobuzState                 `json:"qobuz"`
	Monitor   map[string]string          `json:"monitor"`
	Playlists map[string]PlaylistSyncRef `json:"playlists"`
	Meta      map[string]string          `json:"meta,omitempty"`
}

type SpotifyState struct {
	ClientID     string    `json:"client_id,omitempty"`
	AccessToken  string    `json:"access_token,omitempty"`
	RefreshToken string    `json:"refresh_token,omitempty"`
	ExpiresAt    time.Time `json:"expires_at,omitempty"`
	Scope        string    `json:"scope,omitempty"`
}

type QobuzState struct {
	Username    string `json:"username,omitempty"`
	Password    string `json:"password,omitempty"`
	AccessToken string `json:"access_token,omitempty"`
}

type PlaylistSyncRef struct {
	SourceName      string `json:"source_name,omitempty"`
	QobuzPlaylistID int64  `json:"qobuz_playlist_id,omitempty"`
	Fingerprint     string `json:"fingerprint,omitempty"`
}

func DefaultPath() string {
	home, err := os.UserHomeDir()
	if err != nil || strings.TrimSpace(home) == "" {
		return ".qtransfer-session.json"
	}
	return filepath.Join(home, ".config", "qtransfer", "session.json")
}

func ResolvePath(path string) string {
	path = strings.TrimSpace(path)
	if path == "" {
		return DefaultPath()
	}
	if path == "~" {
		return DefaultPath()
	}
	if strings.HasPrefix(path, "~/") {
		home, err := os.UserHomeDir()
		if err == nil && home != "" {
			return filepath.Join(home, path[2:])
		}
	}
	return path
}

func Load(path string) (Data, error) {
	path = ResolvePath(path)
	b, err := os.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return Data{}, nil
		}
		return Data{}, err
	}

	var d Data
	if err := json.Unmarshal(b, &d); err != nil {
		return Data{}, fmt.Errorf("parse session file: %w", err)
	}
	if d.Monitor == nil {
		d.Monitor = map[string]string{}
	}
	if d.Playlists == nil {
		d.Playlists = map[string]PlaylistSyncRef{}
	}
	return d, nil
}

func Save(path string, data Data) error {
	path = ResolvePath(path)
	dir := filepath.Dir(path)
	if err := os.MkdirAll(dir, 0o700); err != nil {
		return fmt.Errorf("create session directory: %w", err)
	}

	b, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		return fmt.Errorf("encode session: %w", err)
	}

	tmp := path + ".tmp"
	if err := os.WriteFile(tmp, b, 0o600); err != nil {
		return fmt.Errorf("write session temp file: %w", err)
	}
	if err := os.Rename(tmp, path); err != nil {
		return fmt.Errorf("replace session file: %w", err)
	}
	return nil
}
