package sync

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// youtrackConnector talks to YouTrack's REST API with a permanent token.
type youtrackConnector struct {
	baseURL    string
	projectKey string
	token      string
}

func (y *youtrackConnector) Authorize(req *http.Request) {
	req.Header.Set("Authorization", "Bearer "+y.token)
}

func (y *youtrackConnector) TestConnection(ctx context.Context) error {
	var me struct {
		Login string `json:"login"`
	}
	if err := getJSON(ctx, y.Authorize, y.baseURL+"/api/users/me?fields=login", &me); err != nil {
		return fmt.Errorf("youtrack /api/users/me: %w", err)
	}
	return nil
}

type ytIssue struct {
	IDReadable  string `json:"idReadable"`
	Summary     string `json:"summary"`
	Description string `json:"description"`
	Attachments []struct {
		Name     string `json:"name"`
		URL      string `json:"url"`
		MimeType string `json:"mimeType"`
	} `json:"attachments"`
	CustomFields []struct {
		Name  string `json:"name"`
		Value any    `json:"value"`
	} `json:"customFields"`
}

func (y *youtrackConnector) query(ctx context.Context, q, fields string) ([]ytIssue, error) {
	var all []ytIssue
	for skip := 0; ; skip += 100 {
		var page []ytIssue
		u := fmt.Sprintf("%s/api/issues?query=%s&fields=%s&$top=100&$skip=%d",
			y.baseURL, url.QueryEscape(q), url.QueryEscape(fields), skip)
		if err := getJSON(ctx, y.Authorize, u, &page); err != nil {
			return nil, fmt.Errorf("youtrack issues: %w", err)
		}
		all = append(all, page...)
		if len(page) < 100 {
			return all, nil
		}
	}
}

const ytFields = "idReadable,summary,description,attachments(name,url,mimeType),customFields(name,value(name))"

func (y *youtrackConnector) FetchUpdated(ctx context.Context, identity string, since time.Time) ([]Ticket, error) {
	q := fmt.Sprintf("assignee: %s project: %s updated: %s .. *",
		identity, y.projectKey, since.UTC().Format("2006-01-02T15:04"))
	issues, err := y.query(ctx, q, ytFields)
	if err != nil {
		return nil, err
	}
	out := make([]Ticket, 0, len(issues))
	for _, is := range issues {
		out = append(out, y.toTicket(is))
	}
	return out, nil
}

func (y *youtrackConnector) toTicket(is ytIssue) Ticket {
	issueType := "task"
	for _, cf := range is.CustomFields {
		if cf.Name == "Type" {
			if v, ok := cf.Value.(map[string]any); ok {
				if name, ok := v["name"].(string); ok {
					issueType = ytType(name)
				}
			}
		}
	}
	t := Ticket{
		Key:         is.IDReadable,
		URL:         y.baseURL + "/issue/" + is.IDReadable,
		Type:        issueType,
		Title:       is.Summary,
		Description: is.Description,
		Raw:         map[string]any{"idReadable": is.IDReadable},
	}
	for _, a := range is.Attachments {
		u := a.URL
		if strings.HasPrefix(u, "/") {
			u = y.baseURL + u
		}
		t.Attachments = append(t.Attachments, TicketAttachment{Name: a.Name, URL: u, MimeType: a.MimeType})
	}
	return t
}

func (y *youtrackConnector) ListAssignedKeys(ctx context.Context, identity string) ([]string, error) {
	q := fmt.Sprintf("assignee: %s project: %s", identity, y.projectKey)
	issues, err := y.query(ctx, q, "idReadable")
	if err != nil {
		return nil, err
	}
	keys := make([]string, len(issues))
	for i, is := range issues {
		keys[i] = is.IDReadable
	}
	return keys, nil
}

func ytType(name string) string {
	switch strings.ToLower(name) {
	case "epic":
		return "epic"
	case "story", "user story", "feature":
		return "story"
	default:
		return "task"
	}
}
