package richtext

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
)

const userParagraphStylesPath = "/.ultidrive/docs-user-paragraph-styles.json"

type UltiDocParagraphStyleDefinition struct {
	ID            string  `json:"id"`
	Name          string  `json:"name"`
	Scope         string  `json:"scope"`
	BasedOn       string  `json:"basedOn,omitempty"`
	BlockType     string  `json:"blockType"`
	Level         *int    `json:"level,omitempty"`
	FontFamily    string  `json:"fontFamily,omitempty"`
	FontSizePx    float64 `json:"fontSizePx,omitempty"`
	Bold          *bool   `json:"bold,omitempty"`
	Italic        *bool   `json:"italic,omitempty"`
	Underline     *bool   `json:"underline,omitempty"`
	Color         string  `json:"color,omitempty"`
	TextAlign       string  `json:"textAlign,omitempty"`
	LineHeight      float64 `json:"lineHeight,omitempty"`
	SpaceBeforePt   float64 `json:"spaceBeforePt,omitempty"`
	SpaceAfterPt    float64 `json:"spaceAfterPt,omitempty"`
}

type UltiDocParagraphStyles struct {
	Definitions map[string]UltiDocParagraphStyleDefinition `json:"definitions"`
}

type DocsFontDefinition struct {
	Name   string `json:"name"`
	Stack  string `json:"stack"`
	Source string `json:"source,omitempty"`
	URL    string `json:"url,omitempty"`
}

var defaultDocsFonts = []DocsFontDefinition{
	{Name: "Arial", Stack: "Arial, Helvetica, sans-serif", Source: "system"},
	{Name: "Calibri", Stack: "Calibri, Candara, Segoe, sans-serif", Source: "system"},
	{Name: "Comic Sans MS", Stack: `"Comic Sans MS", cursive, sans-serif`, Source: "system"},
	{Name: "Courier New", Stack: `"Courier New", Courier, monospace`, Source: "system"},
	{Name: "Georgia", Stack: "Georgia, serif", Source: "system"},
	{Name: "Times New Roman", Stack: `"Times New Roman", Times, serif`, Source: "system"},
	{Name: "Trebuchet MS", Stack: `"Trebuchet MS", Helvetica, sans-serif`, Source: "system"},
	{Name: "Verdana", Stack: "Verdana, Geneva, sans-serif", Source: "system"},
}

func defaultUltiDocParagraphStyles() UltiDocParagraphStyles {
	level := func(n int) *int { v := n; return &v }
	bold := func(v bool) *bool { return &v }
	italic := func(v bool) *bool { return &v }
	return UltiDocParagraphStyles{
		Definitions: map[string]UltiDocParagraphStyleDefinition{
			"normal":    {ID: "normal", Name: "Normal", Scope: "document", BlockType: "paragraph", FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 11, LineHeight: 1.15},
			"title":     {ID: "title", Name: "Titre", Scope: "document", BlockType: "paragraph", FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 26, LineHeight: 1.15},
			"subtitle":  {ID: "subtitle", Name: "Sous-titre", Scope: "document", BlockType: "paragraph", FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 15, Color: "#666666", LineHeight: 1.15},
			"heading1":  {ID: "heading1", Name: "Titre 1", Scope: "document", BlockType: "heading", Level: level(1), FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 20, LineHeight: 1.15},
			"heading2":  {ID: "heading2", Name: "Titre 2", Scope: "document", BlockType: "heading", Level: level(2), FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 16, LineHeight: 1.15},
			"heading3":  {ID: "heading3", Name: "Titre 3", Scope: "document", BlockType: "heading", Level: level(3), FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 14, LineHeight: 1.15},
			"heading4":  {ID: "heading4", Name: "Titre 4", Scope: "document", BlockType: "heading", Level: level(4), FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 12, Bold: bold(true), LineHeight: 1.15},
			"heading5":  {ID: "heading5", Name: "Titre 5", Scope: "document", BlockType: "heading", Level: level(5), FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 11, Bold: bold(true), LineHeight: 1.15},
			"heading6":  {ID: "heading6", Name: "Titre 6", Scope: "document", BlockType: "heading", Level: level(6), FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 11, Italic: italic(true), LineHeight: 1.15},
		},
	}
}

func (s *Service) ListFonts(_ context.Context) []DocsFontDefinition {
	out := make([]DocsFontDefinition, len(defaultDocsFonts))
	copy(out, defaultDocsFonts)
	return out
}

func (s *Service) LoadUserParagraphStyles(ctx context.Context, ncUser string) (UltiDocParagraphStyles, error) {
	body, err := s.LoadDocument(ctx, ncUser, userParagraphStylesPath)
	if err != nil || len(body) == 0 {
		return UltiDocParagraphStyles{Definitions: map[string]UltiDocParagraphStyleDefinition{}}, nil
	}
	var styles UltiDocParagraphStyles
	if err := json.Unmarshal(body, &styles); err != nil {
		return UltiDocParagraphStyles{}, fmt.Errorf("parse user paragraph styles: %w", err)
	}
	if styles.Definitions == nil {
		styles.Definitions = map[string]UltiDocParagraphStyleDefinition{}
	}
	return styles, nil
}

func (s *Service) SaveUserParagraphStyles(ctx context.Context, ncUser string, styles UltiDocParagraphStyles) error {
	if styles.Definitions == nil {
		styles.Definitions = map[string]UltiDocParagraphStyleDefinition{}
	}
	payload, err := json.MarshalIndent(styles, "", "  ")
	if err != nil {
		return err
	}
	return s.nc.Upload(ctx, ncUser, userParagraphStylesPath, strings.NewReader(string(payload)), "application/json")
}

func (s *Service) loadParagraphStylesFromSidecar(ctx context.Context, ncUser, canonical string) (json.RawMessage, error) {
	raw, err := s.LoadDocument(ctx, ncUser, canonical)
	if err != nil || len(raw) == 0 {
		return nil, err
	}
	doc, err := ParseUltiDoc(raw)
	if err != nil || doc.ParagraphStyles == nil {
		return nil, err
	}
	return json.Marshal(doc.ParagraphStyles)
}
