package storage

import (
	"context"
	"errors"

	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgconn"
	"github.com/nomadcode/nomadcode-core/internal/db"
)

// pgUniqueViolation is the SQLSTATE code Postgres returns for a unique
// constraint violation.
const pgUniqueViolation = "23505"

// ErrRoadmapSyncIdentityNotFound is returned when no identity row matches the
// lookup key.
var ErrRoadmapSyncIdentityNotFound = errors.New("roadmap sync identity not found")

// ErrRoadmapSyncIdentityConflict is returned when the incoming provider work
// item and Milestone path do not agree with an existing identity row: either
// the Milestone path is already bound to a different provider work item, or the
// provider work item is already bound to a different Milestone path. Returning a
// typed conflict keeps a re-process from silently rebinding one identity onto
// another instead of hitting a raw unique-index violation.
var ErrRoadmapSyncIdentityConflict = errors.New("roadmap sync identity conflict")

// GetRoadmapSyncIdentityByWorkItem returns the identity bound to a provider work
// item, or ErrRoadmapSyncIdentityNotFound when none exists.
func (s *Store) GetRoadmapSyncIdentityByWorkItem(ctx context.Context, args db.GetRoadmapSyncIdentityByWorkItemParams) (db.RoadmapSyncIdentity, error) {
	row, err := s.queries.GetRoadmapSyncIdentityByWorkItem(ctx, args)
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return db.RoadmapSyncIdentity{}, ErrRoadmapSyncIdentityNotFound
		}
		return db.RoadmapSyncIdentity{}, err
	}
	return row, nil
}

// GetRoadmapSyncIdentityByMilestoneID returns the identity bound to a roadmap
// Milestone ID (filename slug), or ErrRoadmapSyncIdentityNotFound when none exists.
func (s *Store) GetRoadmapSyncIdentityByMilestoneID(ctx context.Context, milestoneID string) (db.RoadmapSyncIdentity, error) {
	row, err := s.queries.GetRoadmapSyncIdentityByMilestoneID(ctx, milestoneID)
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return db.RoadmapSyncIdentity{}, ErrRoadmapSyncIdentityNotFound
		}
		return db.RoadmapSyncIdentity{}, err
	}
	return row, nil
}

// UpsertRoadmapSyncIdentity persists an identity keyed on the milestone canonical id,
// reusing the existing row when the same Milestone slug is re-processed.
//
// Conflict protection is atomic in a single statement. If a different milestone
// tries to reuse the same provider work item, it will hit the provider work item unique constraint,
// returning ErrRoadmapSyncIdentityConflict.
func (s *Store) UpsertRoadmapSyncIdentity(ctx context.Context, args db.UpsertRoadmapSyncIdentityByMilestoneIDParams) (db.RoadmapSyncIdentity, error) {
	row, err := s.queries.UpsertRoadmapSyncIdentityByMilestoneID(ctx, args)
	if err != nil {
		// If another row already has the same provider + tenant + project + work_item_id,
		// the unique constraint ux_roadmap_sync_identities_provider_work_item is violated.
		var pgErr *pgconn.PgError
		if errors.As(err, &pgErr) && pgErr.Code == pgUniqueViolation {
			return db.RoadmapSyncIdentity{}, ErrRoadmapSyncIdentityConflict
		}
		return db.RoadmapSyncIdentity{}, err
	}
	return row, nil
}
