package store

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"time"

	"github.com/google/uuid"
)

// Space operations

func (r *sqliteRepo) CreateSpace(ctx context.Context, space *Space) (*Space, error) {
	now := utcNow()
	space.ID = uuid.NewString()
	space.CreatedAt = now
	space.UpdatedAt = now

	_, err := r.db.ExecContext(ctx,
		`INSERT INTO spaces (id, slug, name, description, is_public, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)`,
		space.ID, space.Slug, space.Name, space.Description, space.IsPublic, space.CreatedAt, space.UpdatedAt)
	if err != nil {
		return nil, fmt.Errorf("store: create space: %w", err)
	}
	return space, nil
}

func (r *sqliteRepo) GetSpaceBySlug(ctx context.Context, slug string) (*Space, error) {
	row := r.db.QueryRowContext(ctx,
		`SELECT id, slug, name, description, is_public, created_at, updated_at FROM spaces WHERE slug = ?`, slug)
	space := &Space{}
	if err := row.Scan(&space.ID, &space.Slug, &space.Name, &space.Description, &space.IsPublic, &space.CreatedAt, &space.UpdatedAt); err != nil {
		return nil, fmt.Errorf("store: get space by slug: %w", err)
	}
	return space, nil
}

func (r *sqliteRepo) UpdateSpace(ctx context.Context, space *Space) (*Space, error) {
	space.UpdatedAt = utcNow()
	_, err := r.db.ExecContext(ctx,
		`UPDATE spaces SET name = ?, description = ?, is_public = ?, updated_at = ? WHERE id = ?`,
		space.Name, space.Description, space.IsPublic, space.UpdatedAt, space.ID)
	if err != nil {
		return nil, fmt.Errorf("store: update space: %w", err)
	}
	return space, nil
}

func (r *sqliteRepo) DeleteSpace(ctx context.Context, id string) error {
	_, err := r.db.ExecContext(ctx, `DELETE FROM spaces WHERE id = ?`, id)
	if err != nil {
		return fmt.Errorf("store: delete space: %w", err)
	}
	return nil
}

func (r *sqliteRepo) ListPublicSpaces(ctx context.Context) ([]*Space, error) {
	rows, err := r.db.QueryContext(ctx,
		`SELECT id, slug, name, description, is_public, created_at, updated_at FROM spaces WHERE is_public = TRUE ORDER BY created_at DESC`)
	if err != nil {
		return nil, fmt.Errorf("store: list public spaces: %w", err)
	}
	defer rows.Close()

	var spaces []*Space
	for rows.Next() {
		space := &Space{}
		if err := rows.Scan(&space.ID, &space.Slug, &space.Name, &space.Description, &space.IsPublic, &space.CreatedAt, &space.UpdatedAt); err != nil {
			return nil, fmt.Errorf("store: scan space: %w", err)
		}
		spaces = append(spaces, space)
	}
	return spaces, rows.Err()
}

// Photo operations

func (r *sqliteRepo) CreatePhoto(ctx context.Context, photo *Photo) (*Photo, error) {
	photo.ID = uuid.NewString()
	photo.CreatedAt = utcNow()

	_, err := r.db.ExecContext(ctx,
		`INSERT INTO photos (id, space_id, url, thumbnail_url, face_count, metadata, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)`,
		photo.ID, photo.SpaceID, photo.URL, photo.ThumbnailURL, photo.FaceCount, photo.Metadata, photo.CreatedAt)
	if err != nil {
		return nil, fmt.Errorf("store: create photo: %w", err)
	}
	return photo, nil
}

func (r *sqliteRepo) GetPhoto(ctx context.Context, id string) (*Photo, error) {
	row := r.db.QueryRowContext(ctx,
		`SELECT id, space_id, url, thumbnail_url, face_count, metadata, created_at FROM photos WHERE id = ?`, id)
	photo := &Photo{}
	if err := row.Scan(&photo.ID, &photo.SpaceID, &photo.URL, &photo.ThumbnailURL, &photo.FaceCount, &photo.Metadata, &photo.CreatedAt); err != nil {
		return nil, fmt.Errorf("store: get photo: %w", err)
	}
	return photo, nil
}

func (r *sqliteRepo) ListPhotosBySpace(ctx context.Context, spaceID string, limit int) ([]*Photo, error) {
	if limit <= 0 {
		limit = 100
	}
	rows, err := r.db.QueryContext(ctx,
		`SELECT id, space_id, url, thumbnail_url, face_count, metadata, created_at FROM photos WHERE space_id = ? ORDER BY created_at DESC LIMIT ?`,
		spaceID, limit)
	if err != nil {
		return nil, fmt.Errorf("store: list photos by space: %w", err)
	}
	defer rows.Close()

	var photos []*Photo
	for rows.Next() {
		photo := &Photo{}
		if err := rows.Scan(&photo.ID, &photo.SpaceID, &photo.URL, &photo.ThumbnailURL, &photo.FaceCount, &photo.Metadata, &photo.CreatedAt); err != nil {
			return nil, fmt.Errorf("store: scan photo: %w", err)
		}
		photos = append(photos, photo)
	}
	return photos, rows.Err()
}

func (r *sqliteRepo) DeletePhoto(ctx context.Context, id string) error {
	_, err := r.db.ExecContext(ctx, `DELETE FROM photos WHERE id = ?`, id)
	if err != nil {
		return fmt.Errorf("store: delete photo: %w", err)
	}
	return nil
}

// Face embedding operations

func (r *sqliteRepo) CreateFaceEmbedding(ctx context.Context, embedding *FaceEmbedding) (*FaceEmbedding, error) {
	embedding.ID = uuid.NewString()
	embedding.CreatedAt = utcNow()

	// Convert embedding to JSON
	embeddingJSON, err := json.Marshal(embedding.Embedding)
	if err != nil {
		return nil, fmt.Errorf("store: marshal face embedding: %w", err)
	}

	_, err = r.db.ExecContext(ctx,
		`INSERT INTO face_embeddings (id, photo_id, space_id, embedding, bounding_box, created_at) VALUES (?, ?, ?, ?, ?, ?)`,
		embedding.ID, embedding.PhotoID, embedding.SpaceID, embeddingJSON, embedding.BoundingBox, embedding.CreatedAt)
	if err != nil {
		return nil, fmt.Errorf("store: create face embedding: %w", err)
	}
	return embedding, nil
}

func (r *sqliteRepo) ListFaceEmbeddingsBySpace(ctx context.Context, spaceID string) ([]*FaceEmbedding, error) {
	rows, err := r.db.QueryContext(ctx,
		`SELECT id, photo_id, space_id, embedding, bounding_box, created_at FROM face_embeddings WHERE space_id = ?`,
		spaceID)
	if err != nil {
		return nil, fmt.Errorf("store: list face embeddings by space: %w", err)
	}
	defer rows.Close()

	var embeddings []*FaceEmbedding
	for rows.Next() {
		embedding := &FaceEmbedding{}
		var embeddingJSON []byte
		if err := rows.Scan(&embedding.ID, &embedding.PhotoID, &embedding.SpaceID, &embeddingJSON, &embedding.BoundingBox, &embedding.CreatedAt); err != nil {
			return nil, fmt.Errorf("store: scan face embedding: %w", err)
		}
		if err := json.Unmarshal(embeddingJSON, &embedding.Embedding); err != nil {
			return nil, fmt.Errorf("store: unmarshal face embedding: %w", err)
		}
		embeddings = append(embeddings, embedding)
	}
	return embeddings, rows.Err()
}

func (r *sqliteRepo) SearchFaceEmbeddings(ctx context.Context, spaceID string, embedding []float32, limit int) ([]*FaceEmbedding, error) {
	if limit <= 0 {
		limit = 10
	}

	// For now, return all embeddings in the space (cosine similarity search would be implemented here)
	// In a real implementation, you'd compute cosine similarity between the query embedding and all embeddings
	embeddings, err := r.ListFaceEmbeddingsBySpace(ctx, spaceID)
	if err != nil {
		return nil, err
	}

	// Return up to limit results
	if len(embeddings) > limit {
		embeddings = embeddings[:limit]
	}
	return embeddings, nil
}

// Rate limiting operations

func (r *sqliteRepo) CheckRateLimit(ctx context.Context, ipAddress, spaceID string, limit, windowMinutes int) (bool, error) {
	// Calculate window start time
	windowStart := time.Now().UTC().Add(-time.Duration(windowMinutes) * time.Minute).Format("2006-01-02T15:04:05.000Z07:00")

	var count int
	err := r.db.QueryRowContext(ctx,
		`SELECT COALESCE(SUM(request_count), 0) FROM rate_limits WHERE ip_address = ? AND space_id = ? AND window_start >= ?`,
		ipAddress, spaceID, windowStart).Scan(&count)
	if err != nil {
		return false, fmt.Errorf("store: check rate limit: %w", err)
	}

	return count < limit, nil
}

func (r *sqliteRepo) IncrementRateLimit(ctx context.Context, ipAddress, spaceID string, windowMinutes int) error {
	// Calculate window start time (rounded to the nearest window)
	now := time.Now().UTC()
	windowStart := now.Add(-time.Duration(now.Minute()%windowMinutes) * time.Minute).Format("2006-01-02T15:04:05.000Z07:00")

	// Check if there's an existing record for this IP, space, and window
	var id string
	err := r.db.QueryRowContext(ctx,
		`SELECT id FROM rate_limits WHERE ip_address = ? AND space_id = ? AND window_start = ?`,
		ipAddress, spaceID, windowStart).Scan(&id)

	if err == sql.ErrNoRows {
		// Create new record
		id = uuid.NewString()
		_, err = r.db.ExecContext(ctx,
			`INSERT INTO rate_limits (id, ip_address, space_id, request_count, window_start, created_at) VALUES (?, ?, ?, 1, ?, ?)`,
			id, ipAddress, spaceID, windowStart, utcNow())
		if err != nil {
			return fmt.Errorf("store: create rate limit: %w", err)
		}
	} else if err == nil {
		// Update existing record
		_, err = r.db.ExecContext(ctx,
			`UPDATE rate_limits SET request_count = request_count + 1 WHERE id = ?`,
			id)
		if err != nil {
			return fmt.Errorf("store: increment rate limit: %w", err)
		}
	} else {
		return fmt.Errorf("store: get rate limit: %w", err)
	}

	return nil
}