package store

import (
	"context"
	"fmt"
	"time"

	"github.com/jackc/pgx/v5"
)

// Connection is one connected video account (data-model.md VIDEO_CONNECTION).
// TokenRef is the opaque SecretStore reference that resolves to the OAuth refresh
// token — never the token itself. ConnectedAt is set by the DB and is read-only
// on writes (UpsertConnection ignores it).
type Connection struct {
	Provider        string
	ProviderAccount string
	TokenRef        string
	Status          string
	ConnectedAt     time.Time
}

// UpsertConnection records (or refreshes) the user's connection to a provider,
// keyed on (user_id, provider): re-connecting the same provider overwrites the
// token_ref/status/account and bumps connected_at, never duplicating. Like every
// access in this package it routes through withUser, so RLS scopes the write to
// the calling user — a connection can only be written for the current user.
func (s *Store) UpsertConnection(ctx context.Context, userID string, c Connection) error {
	return s.withUser(ctx, userID, func(tx pgx.Tx) error {
		if _, err := tx.Exec(ctx,
			`INSERT INTO video_connections
			     (user_id, provider, provider_account, token_ref, status)
			 VALUES ($1, $2, $3, $4, $5)
			 ON CONFLICT (user_id, provider) DO UPDATE SET
			     provider_account = EXCLUDED.provider_account,
			     token_ref        = EXCLUDED.token_ref,
			     status           = EXCLUDED.status,
			     connected_at     = now()`,
			userID, c.Provider, nullIfEmpty(c.ProviderAccount), c.TokenRef, c.Status,
		); err != nil {
			return fmt.Errorf("store: upsert connection: %w", err)
		}
		return nil
	})
}

// ConnectionsForUser returns the user's connections, most-recently-connected
// first. Scoped by user_id via withUser: one user never sees another's.
func (s *Store) ConnectionsForUser(ctx context.Context, userID string) ([]Connection, error) {
	var out []Connection
	if err := s.withUser(ctx, userID, func(tx pgx.Tx) error {
		rows, err := tx.Query(ctx,
			`SELECT provider, COALESCE(provider_account, ''), token_ref, status, connected_at
			   FROM video_connections
			  WHERE user_id = $1
			  ORDER BY connected_at DESC, provider`,
			userID)
		if err != nil {
			return fmt.Errorf("store: connections for user: %w", err)
		}
		defer rows.Close()

		for rows.Next() {
			var c Connection
			if err := rows.Scan(&c.Provider, &c.ProviderAccount, &c.TokenRef, &c.Status, &c.ConnectedAt); err != nil {
				return fmt.Errorf("store: scan connection: %w", err)
			}
			out = append(out, c)
		}
		if err := rows.Err(); err != nil {
			return fmt.Errorf("store: iterate connections: %w", err)
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return out, nil
}

// DeleteConnection removes the user's connection to a provider. Deleting an
// absent connection is a no-op (no error).
func (s *Store) DeleteConnection(ctx context.Context, userID, provider string) error {
	return s.withUser(ctx, userID, func(tx pgx.Tx) error {
		if _, err := tx.Exec(ctx,
			`DELETE FROM video_connections WHERE user_id = $1 AND provider = $2`,
			userID, provider); err != nil {
			return fmt.Errorf("store: delete connection: %w", err)
		}
		return nil
	})
}

// nullIfEmpty maps "" to a SQL NULL so an unknown provider_account is stored as
// NULL (the column is nullable) rather than an empty string.
func nullIfEmpty(s string) *string {
	if s == "" {
		return nil
	}
	return &s
}
