package database

import (
	"database/sql"
	"fmt"
	"strings"
	"time"

	"github.com/google/uuid"
)

// ValidProjectFactEdgeTypes 项目事实图允许的边类型。
var ValidProjectFactEdgeTypes = map[string]struct{}{
	"depends_on":    {},
	"leads_to":      {},
	"enables":       {},
	"exploits":      {},
	"discovered_on": {},
	"contains":      {},
	"part_of":       {},
	"supports":      {},
}

// ProjectFactEdge 项目事实关系边（source → target）。
type ProjectFactEdge struct {
	ID                   string    `json:"id"`
	ProjectID            string    `json:"project_id"`
	SourceFactKey        string    `json:"source_fact_key"`
	TargetFactKey        string    `json:"target_fact_key"`
	EdgeType             string    `json:"edge_type"`
	Confidence           string    `json:"confidence"` // confirmed | tentative | deprecated
	SourceConversationID string    `json:"source_conversation_id,omitempty"`
	CreatedAt            time.Time `json:"created_at"`
	UpdatedAt            time.Time `json:"updated_at"`
}

// ProjectFactEdgeInput 写入边时的输入（出边：source → To）。
type ProjectFactEdgeInput struct {
	To         string `json:"to"`
	Type       string `json:"type"`
	Confidence string `json:"confidence,omitempty"`
}

// ProjectFactEdgeFromInput 写入入边时的输入（From → 当前事实）。
type ProjectFactEdgeFromInput struct {
	From       string `json:"from"`
	Type       string `json:"type"`
	Confidence string `json:"confidence,omitempty"`
}

// ProjectFactGraphNode 图 API 节点。
type ProjectFactGraphNode struct {
	ID         string `json:"id"`
	FactKey    string `json:"fact_key"`
	Category   string `json:"category"`
	Label      string `json:"label"`   // 图节点短标签（截断）
	Summary    string `json:"summary"` // 完整摘要（侧栏等详情用）
	Confidence string `json:"confidence"`
	Type       string `json:"type"`
	Pinned     bool   `json:"pinned"`
}

// ProjectFactGraphEdge 图 API 边。
type ProjectFactGraphEdge struct {
	ID         string `json:"id"`
	Source     string `json:"source"`
	Target     string `json:"target"`
	Type       string `json:"type"`
	Confidence string `json:"confidence"`
}

// ProjectFactGraph 项目事实图。
type ProjectFactGraph struct {
	Nodes []ProjectFactGraphNode `json:"nodes"`
	Edges []ProjectFactGraphEdge `json:"edges"`
}

// ValidateProjectFactEdgeType 校验边类型。
func ValidateProjectFactEdgeType(edgeType string) error {
	edgeType = strings.TrimSpace(strings.ToLower(edgeType))
	if edgeType == "" {
		return fmt.Errorf("edge type 不能为空")
	}
	if _, ok := ValidProjectFactEdgeTypes[edgeType]; !ok {
		return fmt.Errorf("无效的 edge type: %s", edgeType)
	}
	return nil
}

func normalizeEdgeConfidence(confidence string) string {
	confidence = strings.TrimSpace(strings.ToLower(confidence))
	switch confidence {
	case "confirmed", "deprecated":
		return confidence
	default:
		return "tentative"
	}
}

// ListProjectFactEdgesByProject 列出项目全部边。
func (db *DB) ListProjectFactEdgesByProject(projectID string) ([]*ProjectFactEdge, error) {
	rows, err := db.Query(
		`SELECT id, project_id, source_fact_key, target_fact_key, edge_type, confidence,
		        COALESCE(source_conversation_id,''), created_at, updated_at
		   FROM project_fact_edges
		  WHERE project_id = ?
		  ORDER BY created_at ASC, rowid ASC`,
		projectID,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return scanProjectFactEdges(rows)
}

// ListOutgoingProjectFactEdges 列出某事实的全部出边。
func (db *DB) ListOutgoingProjectFactEdges(projectID, sourceFactKey string) ([]*ProjectFactEdge, error) {
	rows, err := db.Query(
		`SELECT id, project_id, source_fact_key, target_fact_key, edge_type, confidence,
		        COALESCE(source_conversation_id,''), created_at, updated_at
		   FROM project_fact_edges
		  WHERE project_id = ? AND source_fact_key = ?
		  ORDER BY created_at ASC, rowid ASC`,
		projectID, sourceFactKey,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return scanProjectFactEdges(rows)
}

// ListIncomingProjectFactEdges 列出某事实的全部入边。
func (db *DB) ListIncomingProjectFactEdges(projectID, targetFactKey string) ([]*ProjectFactEdge, error) {
	rows, err := db.Query(
		`SELECT id, project_id, source_fact_key, target_fact_key, edge_type, confidence,
		        COALESCE(source_conversation_id,''), created_at, updated_at
		   FROM project_fact_edges
		  WHERE project_id = ? AND target_fact_key = ?
		  ORDER BY created_at ASC, rowid ASC`,
		projectID, targetFactKey,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return scanProjectFactEdges(rows)
}

// ReplaceOutgoingProjectFactEdges 替换某事实的全部出边（links 省略时不调用）。
func (db *DB) ReplaceOutgoingProjectFactEdges(projectID, sourceFactKey, sourceConversationID string, inputs []ProjectFactEdgeInput) error {
	sourceFactKey = strings.TrimSpace(sourceFactKey)
	if sourceFactKey == "" {
		return fmt.Errorf("source_fact_key 不能为空")
	}
	if _, err := db.Exec(
		`DELETE FROM project_fact_edges WHERE project_id = ? AND source_fact_key = ?`,
		projectID, sourceFactKey,
	); err != nil {
		return fmt.Errorf("清除旧边失败: %w", err)
	}
	for _, in := range inputs {
		target := strings.TrimSpace(in.To)
		if target == "" {
			continue
		}
		if err := ValidateFactKey(target); err != nil {
			return fmt.Errorf("target fact_key 无效 (%s): %w", target, err)
		}
		if target == sourceFactKey {
			return fmt.Errorf("边不能指向自身: %s", sourceFactKey)
		}
		if err := ValidateProjectFactEdgeType(in.Type); err != nil {
			return err
		}
		edge := &ProjectFactEdge{
			ID:                   uuid.New().String(),
			ProjectID:            projectID,
			SourceFactKey:        sourceFactKey,
			TargetFactKey:        target,
			EdgeType:             strings.ToLower(strings.TrimSpace(in.Type)),
			Confidence:           normalizeEdgeConfidence(in.Confidence),
			SourceConversationID: sourceConversationID,
			CreatedAt:            time.Now(),
			UpdatedAt:            time.Now(),
		}
		if err := db.insertProjectFactEdge(edge); err != nil {
			return err
		}
	}
	return nil
}

// ReplaceIncomingProjectFactEdges 替换某事实的全部入边（From 为来源 fact_key）。
func (db *DB) ReplaceIncomingProjectFactEdges(projectID, targetFactKey string, inputs []ProjectFactEdgeFromInput) error {
	targetFactKey = strings.TrimSpace(targetFactKey)
	if targetFactKey == "" {
		return fmt.Errorf("target_fact_key 不能为空")
	}
	if _, err := db.Exec(
		`DELETE FROM project_fact_edges WHERE project_id = ? AND target_fact_key = ?`,
		projectID, targetFactKey,
	); err != nil {
		return fmt.Errorf("清除旧入边失败: %w", err)
	}
	for _, in := range inputs {
		source := strings.TrimSpace(in.From)
		if source == "" {
			continue
		}
		if err := ValidateFactKey(source); err != nil {
			return fmt.Errorf("source fact_key 无效 (%s): %w", source, err)
		}
		if source == targetFactKey {
			return fmt.Errorf("边不能指向自身: %s", targetFactKey)
		}
		if err := ValidateProjectFactEdgeType(in.Type); err != nil {
			return err
		}
		sourceConversationID := ""
		if srcFact, err := db.GetProjectFactByKey(projectID, source); err == nil && srcFact != nil {
			sourceConversationID = srcFact.SourceConversationID
		}
		edge := &ProjectFactEdge{
			ID:                   uuid.New().String(),
			ProjectID:            projectID,
			SourceFactKey:        source,
			TargetFactKey:        targetFactKey,
			EdgeType:             strings.ToLower(strings.TrimSpace(in.Type)),
			Confidence:           normalizeEdgeConfidence(in.Confidence),
			SourceConversationID: sourceConversationID,
			CreatedAt:            time.Now(),
			UpdatedAt:            time.Now(),
		}
		if err := db.insertProjectFactEdge(edge); err != nil {
			return err
		}
	}
	return nil
}

// GetProjectFactEdge 按 ID 获取边。
func (db *DB) GetProjectFactEdge(edgeID string) (*ProjectFactEdge, error) {
	var e ProjectFactEdge
	var createdAt, updatedAt string
	err := db.QueryRow(
		`SELECT id, project_id, source_fact_key, target_fact_key, edge_type, confidence,
		        COALESCE(source_conversation_id,''), created_at, updated_at
		   FROM project_fact_edges WHERE id = ?`, edgeID,
	).Scan(&e.ID, &e.ProjectID, &e.SourceFactKey, &e.TargetFactKey, &e.EdgeType, &e.Confidence,
		&e.SourceConversationID, &createdAt, &updatedAt)
	if err != nil {
		return nil, fmt.Errorf("边不存在")
	}
	e.CreatedAt = parseDBTime(createdAt)
	e.UpdatedAt = parseDBTime(updatedAt)
	return &e, nil
}

// AddProjectFactEdge 新增单条边（已存在则更新 confidence）。
func (db *DB) AddProjectFactEdge(projectID string, in ProjectFactEdgeInput, sourceFactKey, sourceConversationID string) (*ProjectFactEdge, error) {
	sourceFactKey = strings.TrimSpace(sourceFactKey)
	target := strings.TrimSpace(in.To)
	if sourceFactKey == "" || target == "" {
		return nil, fmt.Errorf("source 与 target 必填")
	}
	if sourceFactKey == target {
		return nil, fmt.Errorf("边不能指向自身")
	}
	if err := ValidateProjectFactEdgeType(in.Type); err != nil {
		return nil, err
	}
	if err := ValidateFactKey(target); err != nil {
		return nil, err
	}
	now := time.Now()
	e := &ProjectFactEdge{
		ID:                   uuid.New().String(),
		ProjectID:            projectID,
		SourceFactKey:        sourceFactKey,
		TargetFactKey:        target,
		EdgeType:             strings.ToLower(strings.TrimSpace(in.Type)),
		Confidence:           normalizeEdgeConfidence(in.Confidence),
		SourceConversationID: sourceConversationID,
		CreatedAt:            now,
		UpdatedAt:            now,
	}
	_, err := db.Exec(
		`INSERT INTO project_fact_edges (
			id, project_id, source_fact_key, target_fact_key, edge_type, confidence,
			source_conversation_id, created_at, updated_at
		) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
		ON CONFLICT(project_id, source_fact_key, target_fact_key, edge_type)
		DO UPDATE SET confidence = excluded.confidence, updated_at = excluded.updated_at`,
		e.ID, e.ProjectID, e.SourceFactKey, e.TargetFactKey, e.EdgeType, e.Confidence,
		nullIfEmpty(e.SourceConversationID), e.CreatedAt, e.UpdatedAt,
	)
	if err != nil {
		return nil, fmt.Errorf("添加边失败: %w", err)
	}
	// 返回最新
	rows, err := db.Query(
		`SELECT id, project_id, source_fact_key, target_fact_key, edge_type, confidence,
		        COALESCE(source_conversation_id,''), created_at, updated_at
		   FROM project_fact_edges
		  WHERE project_id = ? AND source_fact_key = ? AND target_fact_key = ? AND edge_type = ?`,
		projectID, sourceFactKey, target, e.EdgeType,
	)
	if err != nil {
		return e, nil
	}
	defer rows.Close()
	list, err := scanProjectFactEdges(rows)
	if err != nil || len(list) == 0 {
		return e, nil
	}
	return list[0], nil
}

// DeleteProjectFactEdge 删除单条边。
func (db *DB) DeleteProjectFactEdge(edgeID string) error {
	res, err := db.Exec(`DELETE FROM project_fact_edges WHERE id = ?`, edgeID)
	if err != nil {
		return err
	}
	n, _ := res.RowsAffected()
	if n == 0 {
		return fmt.Errorf("边不存在")
	}
	return nil
}

func (db *DB) insertProjectFactEdge(e *ProjectFactEdge) error {
	_, err := db.Exec(
		`INSERT INTO project_fact_edges (
			id, project_id, source_fact_key, target_fact_key, edge_type, confidence,
			source_conversation_id, created_at, updated_at
		) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		e.ID, e.ProjectID, e.SourceFactKey, e.TargetFactKey, e.EdgeType, e.Confidence,
		nullIfEmpty(e.SourceConversationID), e.CreatedAt, e.UpdatedAt,
	)
	if err != nil {
		return fmt.Errorf("写入边失败: %w", err)
	}
	return nil
}

// RenameProjectFactKeyEdges 事实 key 变更时同步边上的引用。
func (db *DB) RenameProjectFactKeyEdges(projectID, oldKey, newKey string) error {
	oldKey = strings.TrimSpace(oldKey)
	newKey = strings.TrimSpace(newKey)
	if oldKey == "" || newKey == "" || oldKey == newKey {
		return nil
	}
	now := time.Now()
	if _, err := db.Exec(
		`UPDATE project_fact_edges SET source_fact_key = ?, updated_at = ?
		  WHERE project_id = ? AND source_fact_key = ?`,
		newKey, now, projectID, oldKey,
	); err != nil {
		return err
	}
	_, err := db.Exec(
		`UPDATE project_fact_edges SET target_fact_key = ?, updated_at = ?
		  WHERE project_id = ? AND target_fact_key = ?`,
		newKey, now, projectID, oldKey,
	)
	return err
}

// DeleteProjectFactEdgesForKey 删除与某 fact_key 相关的全部边。
func (db *DB) DeleteProjectFactEdgesForKey(projectID, factKey string) error {
	_, err := db.Exec(
		`DELETE FROM project_fact_edges
		  WHERE project_id = ? AND (source_fact_key = ? OR target_fact_key = ?)`,
		projectID, factKey, factKey,
	)
	return err
}

// DeprecateProjectFactEdgesForKey 将关联边标记为 deprecated。
func (db *DB) DeprecateProjectFactEdgesForKey(projectID, factKey string) error {
	now := time.Now()
	_, err := db.Exec(
		`UPDATE project_fact_edges SET confidence = 'deprecated', updated_at = ?
		  WHERE project_id = ? AND (source_fact_key = ? OR target_fact_key = ?)
		    AND confidence != 'deprecated'`,
		now, projectID, factKey, factKey,
	)
	return err
}

func scanProjectFactEdges(rows *sql.Rows) ([]*ProjectFactEdge, error) {
	var out []*ProjectFactEdge
	for rows.Next() {
		var e ProjectFactEdge
		var createdAt, updatedAt string
		if err := rows.Scan(
			&e.ID, &e.ProjectID, &e.SourceFactKey, &e.TargetFactKey, &e.EdgeType, &e.Confidence,
			&e.SourceConversationID, &createdAt, &updatedAt,
		); err != nil {
			return nil, err
		}
		e.CreatedAt = parseDBTime(createdAt)
		e.UpdatedAt = parseDBTime(updatedAt)
		out = append(out, &e)
	}
	return out, rows.Err()
}
