package store

import (
	"context"
	"fmt"
	"strings"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"

	thoughttypes "git.warky.dev/wdevs/amcs/internal/types"
)

func (db *DB) CreateProject(ctx context.Context, name, description string) (thoughttypes.Project, error) {
	row := db.pool.QueryRow(ctx, `
		insert into projects (name, description)
		values ($1, $2)
		returning guid, name, description, created_at, last_active_at
	`, name, description)

	var project thoughttypes.Project
	if err := row.Scan(&project.ID, &project.Name, &project.Description, &project.CreatedAt, &project.LastActiveAt); err != nil {
		return thoughttypes.Project{}, fmt.Errorf("create project: %w", err)
	}
	return project, nil
}

func (db *DB) GetProject(ctx context.Context, nameOrID string) (thoughttypes.Project, error) {
	lookup := strings.TrimSpace(nameOrID)

	// Prefer guid lookup when input parses as UUID, but fall back to name lookup
	// so UUID-shaped project names can still be resolved by name.
	if parsedID, err := uuid.Parse(lookup); err == nil {
		project, queryErr := db.getProjectByGUID(ctx, parsedID)
		if queryErr == nil {
			return project, nil
		}
		if queryErr != pgx.ErrNoRows {
			return thoughttypes.Project{}, queryErr
		}
	}

	return db.getProjectByName(ctx, lookup)
}

func (db *DB) getProjectByGUID(ctx context.Context, id uuid.UUID) (thoughttypes.Project, error) {
	row := db.pool.QueryRow(ctx, `
		select guid, name, description, created_at, last_active_at
		from projects
		where guid = $1
	`, id)
	return scanProject(row)
}

func (db *DB) getProjectByName(ctx context.Context, name string) (thoughttypes.Project, error) {
	row := db.pool.QueryRow(ctx, `
		select guid, name, description, created_at, last_active_at
		from projects
		where name = $1
	`, name)
	return scanProject(row)
}

func scanProject(row pgx.Row) (thoughttypes.Project, error) {
	var project thoughttypes.Project
	if err := row.Scan(&project.ID, &project.Name, &project.Description, &project.CreatedAt, &project.LastActiveAt); err != nil {
		if err == pgx.ErrNoRows {
			return thoughttypes.Project{}, err
		}
		return thoughttypes.Project{}, fmt.Errorf("get project: %w", err)
	}
	return project, nil
}

func (db *DB) ListProjects(ctx context.Context) ([]thoughttypes.ProjectSummary, error) {
	rows, err := db.pool.Query(ctx, `
		select p.guid, p.name, p.description, p.created_at, p.last_active_at, count(t.id) as thought_count
		from projects p
		left join thoughts t on t.project_id = p.guid and t.archived_at is null
		group by p.guid, p.name, p.description, p.created_at, p.last_active_at
		order by p.last_active_at desc, p.created_at desc
	`)
	if err != nil {
		return nil, fmt.Errorf("list projects: %w", err)
	}
	defer rows.Close()

	projects := make([]thoughttypes.ProjectSummary, 0)
	for rows.Next() {
		var project thoughttypes.ProjectSummary
		if err := rows.Scan(&project.ID, &project.Name, &project.Description, &project.CreatedAt, &project.LastActiveAt, &project.ThoughtCount); err != nil {
			return nil, fmt.Errorf("scan project summary: %w", err)
		}
		projects = append(projects, project)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate projects: %w", err)
	}
	return projects, nil
}

func (db *DB) TouchProject(ctx context.Context, id uuid.UUID) error {
	tag, err := db.pool.Exec(ctx, `update projects set last_active_at = now() where guid = $1`, id)
	if err != nil {
		return fmt.Errorf("touch project: %w", err)
	}
	if tag.RowsAffected() == 0 {
		return pgx.ErrNoRows
	}
	return nil
}
