package handlers

import (
	"encoding/json"
	"net/http"

	"git.warky.dev/wdevs/whatshooked/pkg/utils"
	"git.warky.dev/wdevs/whatshooked/pkg/whatsapp/businessapi"
	"go.mau.fi/whatsmeow/types"
)

// ListFlows returns all flows for the account.
// POST /api/flows  {"account_id"}
func (h *Handlers) ListFlows(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req struct {
		AccountID string `json:"account_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	baClient, err := h.getBusinessAPIClient(req.AccountID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	resp, err := baClient.ListFlows(r.Context())
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJSON(w, resp)
}

// CreateFlow creates a new flow.
// POST /api/flows/create  {"account_id","name","categories":[...],"endpoint_url"}
func (h *Handlers) CreateFlow(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req struct {
		AccountID string `json:"account_id"`
		businessapi.FlowCreateRequest
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if req.Name == "" || len(req.Categories) == 0 {
		http.Error(w, "name and categories are required", http.StatusBadRequest)
		return
	}

	baClient, err := h.getBusinessAPIClient(req.AccountID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	resp, err := baClient.CreateFlow(r.Context(), req.FlowCreateRequest)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJSON(w, resp)
}

// GetFlow returns details for a single flow.
// POST /api/flows/get  {"account_id","flow_id"}
func (h *Handlers) GetFlow(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req struct {
		AccountID string `json:"account_id"`
		FlowID    string `json:"flow_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if req.FlowID == "" {
		http.Error(w, "flow_id is required", http.StatusBadRequest)
		return
	}

	// validate account exists and is business-api (even though GetFlow doesn't strictly need it, consistency)
	if _, err := h.getBusinessAPIClient(req.AccountID); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	baClient, err := h.getBusinessAPIClient(req.AccountID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	resp, err := baClient.GetFlow(r.Context(), req.FlowID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJSON(w, resp)
}

// UploadFlowAsset uploads a screens JSON to a DRAFT flow.
// POST /api/flows/upload  {"account_id","flow_id","screens_json":"{...}"}
func (h *Handlers) UploadFlowAsset(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req struct {
		AccountID   string `json:"account_id"`
		FlowID      string `json:"flow_id"`
		ScreensJSON string `json:"screens_json"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if req.FlowID == "" || req.ScreensJSON == "" {
		http.Error(w, "flow_id and screens_json are required", http.StatusBadRequest)
		return
	}

	baClient, err := h.getBusinessAPIClient(req.AccountID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if err := baClient.UpdateFlowAssets(r.Context(), req.FlowID, req.ScreensJSON); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJSON(w, map[string]string{"status": "ok"})
}

// PublishFlow publishes a DRAFT flow.
// POST /api/flows/publish  {"account_id","flow_id"}
func (h *Handlers) PublishFlow(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req struct {
		AccountID string `json:"account_id"`
		FlowID    string `json:"flow_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if req.FlowID == "" {
		http.Error(w, "flow_id is required", http.StatusBadRequest)
		return
	}

	baClient, err := h.getBusinessAPIClient(req.AccountID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if err := baClient.PublishFlow(r.Context(), req.FlowID); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJSON(w, map[string]string{"status": "ok"})
}

// DeprecateFlow transitions a PUBLISHED flow to DEPRECATED.
// Deprecated flows block new sessions but remain usable by sessions already in progress.
// POST /api/flows/deprecate  {"account_id","flow_id"}
func (h *Handlers) DeprecateFlow(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req struct {
		AccountID string `json:"account_id"`
		FlowID    string `json:"flow_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if req.FlowID == "" {
		http.Error(w, "flow_id is required", http.StatusBadRequest)
		return
	}

	baClient, err := h.getBusinessAPIClient(req.AccountID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if err := baClient.DeprecateFlow(r.Context(), req.FlowID); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJSON(w, map[string]string{"status": "ok"})
}

// DeleteFlow permanently removes a flow.
// POST /api/flows/delete  {"account_id","flow_id"}
func (h *Handlers) DeleteFlow(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req struct {
		AccountID string `json:"account_id"`
		FlowID    string `json:"flow_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if req.FlowID == "" {
		http.Error(w, "flow_id is required", http.StatusBadRequest)
		return
	}

	baClient, err := h.getBusinessAPIClient(req.AccountID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if err := baClient.DeleteFlow(r.Context(), req.FlowID); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJSON(w, map[string]string{"status": "ok"})
}

// SendFlow sends an interactive flow message.
// POST /api/send/flow  {"account_id","to","flow_id","flow_token","screen_name","data":{...}}
func (h *Handlers) SendFlow(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req struct {
		AccountID  string         `json:"account_id"`
		To         string         `json:"to"`
		FlowID     string         `json:"flow_id"`
		FlowToken  string         `json:"flow_token"`
		ScreenName string         `json:"screen_name"`
		Data       map[string]any `json:"data"`
		Header     string         `json:"header"`
		Body       string         `json:"body"`
		Footer     string         `json:"footer"`
		Dynamic    bool           `json:"dynamic"` // if true, use type "unpublished"
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if req.FlowID == "" || req.Body == "" {
		http.Error(w, "flow_id and body are required", http.StatusBadRequest)
		return
	}

	baClient, err := h.getBusinessAPIClient(req.AccountID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	jid, err := types.ParseJID(utils.FormatPhoneToJID(req.To, h.config.Server.DefaultCountryCode))
	if err != nil {
		http.Error(w, "Invalid phone number", http.StatusBadRequest)
		return
	}

	paramType := "payload"
	if req.Dynamic {
		paramType = "unpublished"
	}

	interactive := &businessapi.InteractiveObject{
		Type: "flow",
		Body: &businessapi.InteractiveBody{Text: req.Body},
		Action: &businessapi.InteractiveAction{
			Name: "flow",
			Parameters: &businessapi.FlowActionParams{
				Type:      paramType,
				FlowToken: req.FlowToken,
				Name:      req.ScreenName,
				Data:      req.Data,
			},
		},
	}
	if req.Header != "" {
		interactive.Header = &businessapi.InteractiveHeader{Type: "text", Text: req.Header}
	}
	if req.Footer != "" {
		interactive.Footer = &businessapi.InteractiveFooter{Text: req.Footer}
	}

	if _, err := baClient.SendInteractive(r.Context(), jid, interactive); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJSON(w, map[string]string{"status": "ok"})
}
