// Package workperform is the app-side client of the Work Performer Service
// (§5.2) — fully independent from the atomizer: separate base URL, token,
// and circuit breaker.
package workperform

import (
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"net/http"
	"time"

	"bountyboard/internal/extsvc"
)

type Attachment struct {
	Name     string `json:"name"`
	URL      string `json:"url"`
	MimeType string `json:"mimeType"`
}

type JobContext struct {
	RepositoryURL string `json:"repositoryUrl,omitempty"`
	Branch        string `json:"branch,omitempty"`
	Instructions  string `json:"instructions,omitempty"`
}

type JobRequest struct {
	TaskID             string       `json:"taskId"`
	Title              string       `json:"title"`
	Description        string       `json:"description"`
	AcceptanceCriteria []string     `json:"acceptanceCriteria"`
	Attachments        []Attachment `json:"attachments"`
	Links              []string     `json:"links"`
	Context            JobContext   `json:"context"`
	CallbackURL        string       `json:"callbackUrl"`
}

type JobAccepted struct {
	JobID  string `json:"jobId"`
	Status string `json:"status"`
}

type JobStatus struct {
	JobID      string `json:"jobId"`
	Status     string `json:"status"` // queued | running | succeeded | failed
	StartedAt  string `json:"startedAt"`
	FinishedAt string `json:"finishedAt"`
}

// Callback is the §5.2 service→app result payload.
type Callback struct {
	JobID     string `json:"jobId"`
	TaskID    string `json:"taskId"`
	Status    string `json:"status"` // succeeded | failed
	Summary   string `json:"summary"`
	Artifacts []struct {
		Name string `json:"name"`
		URL  string `json:"url"`
	} `json:"artifacts"`
	Log string `json:"log"`
}

type Client struct {
	c     *extsvc.Client
	token string
}

func New(baseURL func() string, token string, timeout time.Duration) *Client {
	return &Client{c: extsvc.NewClient("work-performer", baseURL, token, timeout), token: token}
}

func (w *Client) BreakerState() string              { return w.c.BreakerState() }
func (w *Client) Healthy(ctx context.Context) error { return w.c.Healthy(ctx) }

func (w *Client) Submit(ctx context.Context, req JobRequest) (*JobAccepted, error) {
	var out JobAccepted
	if err := w.c.Call(ctx, http.MethodPost, "/v1/jobs", req, &out); err != nil {
		return nil, err
	}
	return &out, nil
}

func (w *Client) Status(ctx context.Context, jobID string) (*JobStatus, error) {
	var out JobStatus
	if err := w.c.Call(ctx, http.MethodGet, "/v1/jobs/"+jobID, nil, &out); err != nil {
		return nil, err
	}
	return &out, nil
}

// Cancel is best effort (§5.2).
func (w *Client) Cancel(ctx context.Context, jobID string) error {
	return w.c.Call(ctx, http.MethodDelete, "/v1/jobs/"+jobID, nil, nil)
}

// VerifySignature checks the §5.2 callback HMAC:
// X-Signature = hex(hmac-sha256(body, WORK_PERFORMER_TOKEN)).
func VerifySignature(token string, body []byte, signature string) bool {
	mac := hmac.New(sha256.New, []byte(token))
	mac.Write(body)
	want := hex.EncodeToString(mac.Sum(nil))
	return hmac.Equal([]byte(want), []byte(signature))
}

// Sign produces the callback signature (used by tests and the mock service).
func Sign(token string, body []byte) string {
	mac := hmac.New(sha256.New, []byte(token))
	mac.Write(body)
	return hex.EncodeToString(mac.Sum(nil))
}
