package rules

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

const WorkflowVersion = 1

type RuleKind string

const (
	RuleKindRule     RuleKind = "rule"
	RuleKindFunction RuleKind = "function"
)

type TriggerType string

const (
	TriggerMessageReceived   TriggerType = "message_received"
	TriggerLabelAdded        TriggerType = "label_added"
	TriggerLabelRemoved      TriggerType = "label_removed"
	TriggerDriveFileCreated  TriggerType = "drive_file_created"
	TriggerDriveFileUpdated  TriggerType = "drive_file_updated"
	TriggerDriveFileDeleted  TriggerType = "drive_file_deleted"
	TriggerDriveFileMoved    TriggerType = "drive_file_moved"
	TriggerDriveShareUpdated TriggerType = "drive_share_updated"
	TriggerContactCreated    TriggerType = "contact_created"
	TriggerContactUpdated    TriggerType = "contact_updated"
	TriggerContactDeleted    TriggerType = "contact_deleted"
)

type Trigger struct {
	Type         TriggerType `json:"type"`
	FolderID     string      `json:"folder_id,omitempty"`
	Label        string      `json:"label,omitempty"`
	AccountID    string      `json:"account_id,omitempty"`
	FolderPath   string      `json:"folder_path,omitempty"`
	ContactLabel string      `json:"contact_label,omitempty"`
}

type TriggerGroup struct {
	Operator string        `json:"operator"` // "or"
	Groups   []TriggerAnd  `json:"groups"`
}

type TriggerAnd struct {
	Operator string    `json:"operator"` // "and"
	Items    []Trigger `json:"items"`
}

type ExecVariable struct {
	Name    string `json:"name"`
	Type    string `json:"type"` // string, number, boolean
	Default string `json:"default,omitempty"`
}

type WorkflowNode struct {
	ID       string          `json:"id"`
	Type     string          `json:"type"`
	Position json.RawMessage `json:"position,omitempty"`
	Data     json.RawMessage `json:"data"`
}

type WorkflowEdge struct {
	ID           string `json:"id"`
	Source       string `json:"source"`
	Target       string `json:"target"`
	SourceHandle string `json:"sourceHandle,omitempty"`
}

type Workflow struct {
	Version   int            `json:"version"`
	Kind      RuleKind       `json:"kind"`
	Triggers  TriggerGroup   `json:"triggers"`
	Variables []ExecVariable `json:"variables,omitempty"`
	Nodes     []WorkflowNode `json:"nodes"`
	Edges     []WorkflowEdge `json:"edges"`
}

type ConditionNodeData struct {
	Field    string `json:"field"`
	Operator string `json:"operator"`
	Value    string `json:"value"`
}

type LabelCheckNodeData struct {
	Label    string `json:"label"`
	Operator string `json:"operator"` // has, not_has
}

type SwitchCase struct {
	Value string `json:"value"`
	Label string `json:"label,omitempty"`
}

type SwitchNodeData struct {
	Field string       `json:"field"`
	Cases []SwitchCase `json:"cases"`
}

type LLMCheckNodeData struct {
	Prompt   string `json:"prompt"`
	Provider string `json:"provider,omitempty"`
	Model    string `json:"model,omitempty"`
}

type ActionItem struct {
	Type  string `json:"type"`
	Value string `json:"value"`
}

type ActionsNodeData struct {
	Actions []ActionItem `json:"actions"`
}

type SetVarNodeData struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type CallRuleNodeData struct {
	RuleID string `json:"rule_id"`
}

type EventContext struct {
	Type         TriggerType
	FolderID     string
	Label        string
	FolderPath   string
	ContactLabel string
	// Drive payload (when domain is drive)
	DriveFileName string
	DriveFilePath string
	DriveMimeType string
	DriveFileSize int64
	DriveIsFolder bool
	// Contact payload (when domain is contacts)
	ContactID     string
	ContactBookID string
	ContactName   string
	ContactEmail  string
	ContactPhone  string
	ContactOrg    string
}

func ParseWorkflow(raw []byte) (*Workflow, error) {
	if len(raw) == 0 || string(raw) == "null" {
		return nil, nil
	}
	var wf Workflow
	if err := json.Unmarshal(raw, &wf); err != nil {
		return nil, fmt.Errorf("parse workflow: %w", err)
	}
	if wf.Version == 0 {
		wf.Version = WorkflowVersion
	}
	if wf.Kind == "" {
		wf.Kind = RuleKindRule
	}
	return &wf, nil
}

func (wf *Workflow) nodeMap() map[string]WorkflowNode {
	m := make(map[string]WorkflowNode, len(wf.Nodes))
	for _, n := range wf.Nodes {
		m[n.ID] = n
	}
	return m
}

func (wf *Workflow) outgoingEdges(nodeID string) []WorkflowEdge {
	var out []WorkflowEdge
	for _, e := range wf.Edges {
		if e.Source == nodeID {
			out = append(out, e)
		}
	}
	return out
}

func (wf *Workflow) nextNode(nodeID, handle string) string {
	for _, e := range wf.Edges {
		if e.Source == nodeID && e.SourceHandle == handle {
			return e.Target
		}
	}
	return ""
}

func (wf *Workflow) nextDefault(nodeID string) string {
	for _, e := range wf.Edges {
		if e.Source == nodeID && e.SourceHandle == "" {
			return e.Target
		}
	}
	return ""
}

func (wf *Workflow) findStartNode() string {
	for _, n := range wf.Nodes {
		if n.Type == "start" {
			return n.ID
		}
	}
	return ""
}

func matchesTriggers(triggers TriggerGroup, msg *Message, evt *EventContext) bool {
	if len(triggers.Groups) == 0 {
		return true
	}
	for _, group := range triggers.Groups {
		if matchesTriggerAnd(group, msg, evt) {
			return true
		}
	}
	return false
}

func matchesTriggerAnd(group TriggerAnd, msg *Message, evt *EventContext) bool {
	if len(group.Items) == 0 {
		return true
	}
	for _, t := range group.Items {
		if !matchTrigger(t, msg, evt) {
			return false
		}
	}
	return true
}

func matchTrigger(t Trigger, msg *Message, evt *EventContext) bool {
	switch t.Type {
	case TriggerMessageReceived:
		if evt != nil && evt.Type != TriggerMessageReceived && evt.Type != "" {
			return false
		}
		if t.AccountID != "" && msg.AccountID != "" && t.AccountID != msg.AccountID {
			return false
		}
		if t.FolderID != "" && msg.FolderID != "" && t.FolderID != msg.FolderID {
			return false
		}
		return true
	case TriggerLabelAdded:
		if evt == nil || evt.Type != TriggerLabelAdded {
			return false
		}
		if t.Label != "" && t.Label != evt.Label {
			return false
		}
		return true
	case TriggerLabelRemoved:
		if evt == nil || evt.Type != TriggerLabelRemoved {
			return false
		}
		if t.Label != "" && t.Label != evt.Label {
			return false
		}
		return true
	case TriggerDriveFileCreated, TriggerDriveFileUpdated, TriggerDriveFileDeleted, TriggerDriveFileMoved, TriggerDriveShareUpdated:
		if evt == nil || evt.Type != t.Type {
			return false
		}
		if t.FolderPath != "" && evt.FolderPath != "" && !strings.HasPrefix(evt.DriveFilePath, t.FolderPath) {
			return false
		}
		return true
	case TriggerContactCreated, TriggerContactUpdated, TriggerContactDeleted:
		if evt == nil || evt.Type != t.Type {
			return false
		}
		if t.ContactLabel != "" && evt.ContactLabel != "" && t.ContactLabel != evt.ContactLabel {
			return false
		}
		return true
	default:
		return false
	}
}
