package postgres

import (
	"context"

	"chat-switchboard/models"

	"github.com/lib/pq"
)

type NoteLinkStore struct{}

func NewNoteLinkStore() *NoteLinkStore { return &NoteLinkStore{} }

func (s *NoteLinkStore) ReplaceLinks(ctx context.Context, sourceNoteID string, links []models.NoteLink) error {
	tx, err := DB.BeginTx(ctx, nil)
	if err != nil {
		return err
	}
	defer tx.Rollback()

	// Delete existing links for this source note
	if _, err := tx.ExecContext(ctx,
		"DELETE FROM note_links WHERE source_note_id = $1", sourceNoteID); err != nil {
		return err
	}

	// Insert new links
	if len(links) > 0 {
		stmt, err := tx.PrepareContext(ctx, `
			INSERT INTO note_links (source_note_id, target_note_id, target_title, display_text, is_transclusion)
			VALUES ($1, $2, $3, $4, $5)
			ON CONFLICT (source_note_id, target_title) DO NOTHING`)
		if err != nil {
			return err
		}
		defer stmt.Close()

		for _, l := range links {
			var targetID interface{}
			if l.TargetNoteID != nil {
				targetID = *l.TargetNoteID
			}
			if _, err := stmt.ExecContext(ctx,
				sourceNoteID, targetID, l.TargetTitle, l.DisplayText, l.IsTransclusion,
			); err != nil {
				return err
			}
		}
	}

	return tx.Commit()
}

func (s *NoteLinkStore) ResolveByTitle(ctx context.Context, userID, targetNoteID, title string) error {
	_, err := DB.ExecContext(ctx, `
		UPDATE note_links
		SET target_note_id = $1
		WHERE target_note_id IS NULL
		AND LOWER(target_title) = LOWER($2)
		AND source_note_id IN (SELECT id FROM notes WHERE user_id = $3)`,
		targetNoteID, title, userID)
	return err
}

func (s *NoteLinkStore) Backlinks(ctx context.Context, noteID string) ([]models.NoteLinkResult, error) {
	rows, err := DB.QueryContext(ctx, `
		SELECT n.id, n.title, n.folder_path, n.updated_at, COALESCE(nl.display_text, '')
		FROM note_links nl
		JOIN notes n ON n.id = nl.source_note_id
		WHERE nl.target_note_id = $1
		ORDER BY n.updated_at DESC`, noteID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var results []models.NoteLinkResult
	for rows.Next() {
		var r models.NoteLinkResult
		if err := rows.Scan(&r.SourceNoteID, &r.Title, &r.FolderPath,
			&r.UpdatedAt, &r.DisplayText); err != nil {
			return nil, err
		}
		results = append(results, r)
	}
	return results, rows.Err()
}

func (s *NoteLinkStore) Graph(ctx context.Context, userID string) (*models.NoteGraph, error) {
	graph := &models.NoteGraph{
		Nodes:      make([]models.NoteGraphNode, 0),
		Edges:      make([]models.NoteGraphEdge, 0),
		Unresolved: make([]models.NoteGraphDangling, 0),
	}

	// Nodes: all user's notes with link counts
	nodeRows, err := DB.QueryContext(ctx, `
		SELECT n.id, n.title, n.folder_path, n.tags, n.updated_at::text,
		       COALESCE(outbound.cnt, 0) + COALESCE(inbound.cnt, 0) AS link_count
		FROM notes n
		LEFT JOIN (
			SELECT source_note_id, COUNT(*) AS cnt FROM note_links
			WHERE target_note_id IS NOT NULL GROUP BY source_note_id
		) outbound ON outbound.source_note_id = n.id
		LEFT JOIN (
			SELECT target_note_id, COUNT(*) AS cnt FROM note_links
			WHERE target_note_id IS NOT NULL GROUP BY target_note_id
		) inbound ON inbound.target_note_id = n.id
		WHERE n.user_id = $1
		ORDER BY n.updated_at DESC`, userID)
	if err != nil {
		return nil, err
	}
	defer nodeRows.Close()

	for nodeRows.Next() {
		var node models.NoteGraphNode
		var tags []string
		if err := nodeRows.Scan(&node.ID, &node.Title, &node.FolderPath,
			pq.Array(&tags), &node.UpdatedAt, &node.LinkCount); err != nil {
			return nil, err
		}
		if tags == nil {
			tags = []string{}
		}
		node.Tags = tags
		graph.Nodes = append(graph.Nodes, node)
	}
	if err := nodeRows.Err(); err != nil {
		return nil, err
	}

	// Resolved edges
	edgeRows, err := DB.QueryContext(ctx, `
		SELECT nl.source_note_id, nl.target_note_id, nl.target_title, nl.is_transclusion
		FROM note_links nl
		JOIN notes n ON n.id = nl.source_note_id
		WHERE n.user_id = $1
		AND nl.target_note_id IS NOT NULL`, userID)
	if err != nil {
		return nil, err
	}
	defer edgeRows.Close()

	for edgeRows.Next() {
		var edge models.NoteGraphEdge
		if err := edgeRows.Scan(&edge.Source, &edge.Target,
			&edge.Title, &edge.IsTransclusion); err != nil {
			return nil, err
		}
		graph.Edges = append(graph.Edges, edge)
	}
	if err := edgeRows.Err(); err != nil {
		return nil, err
	}

	// Unresolved links (dangling)
	danglingRows, err := DB.QueryContext(ctx, `
		SELECT nl.source_note_id, nl.target_title
		FROM note_links nl
		JOIN notes n ON n.id = nl.source_note_id
		WHERE n.user_id = $1
		AND nl.target_note_id IS NULL`, userID)
	if err != nil {
		return nil, err
	}
	defer danglingRows.Close()

	for danglingRows.Next() {
		var d models.NoteGraphDangling
		if err := danglingRows.Scan(&d.Source, &d.Title); err != nil {
			return nil, err
		}
		graph.Unresolved = append(graph.Unresolved, d)
	}

	return graph, danglingRows.Err()
}
