// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package storage // import "miniflux.app/v2/internal/storage"

import (
	"errors"
	"fmt"

	"miniflux.app/v2/internal/crypto"
	"miniflux.app/v2/internal/model"
)

var ErrAPIKeyNotFound = errors.New("store: API Key not found")

// APIKeyExists checks if an API Key with the same description exists.
func (s *Storage) APIKeyExists(userID int64, description string) bool {
	var result bool
	query := `SELECT true FROM api_keys WHERE user_id=$1 AND lower(description)=lower($2) LIMIT 1`
	s.db.QueryRow(query, userID, description).Scan(&result)
	return result
}

// SetAPIKeyUsedTimestamp updates the last used date of an API Key.
func (s *Storage) SetAPIKeyUsedTimestamp(userID int64, token string) error {
	query := `UPDATE api_keys SET last_used_at=now() WHERE user_id=$1 and token=$2`
	_, err := s.db.Exec(query, userID, token)
	if err != nil {
		return fmt.Errorf(`store: unable to update last used date for API key: %v`, err)
	}

	return nil
}

// APIKeys returns all API Keys that belongs to the given user.
func (s *Storage) APIKeys(userID int64) (model.APIKeys, error) {
	query := `
		SELECT
			id, user_id, token, description, last_used_at, created_at
		FROM
			api_keys
		WHERE
			user_id=$1
		ORDER BY description ASC
	`
	rows, err := s.db.Query(query, userID)
	if err != nil {
		return nil, fmt.Errorf(`store: unable to fetch API Keys: %v`, err)
	}
	defer rows.Close()

	apiKeys := make(model.APIKeys, 0)
	for rows.Next() {
		var apiKey model.APIKey
		if err := rows.Scan(
			&apiKey.ID,
			&apiKey.UserID,
			&apiKey.Token,
			&apiKey.Description,
			&apiKey.LastUsedAt,
			&apiKey.CreatedAt,
		); err != nil {
			return nil, fmt.Errorf(`store: unable to fetch API Key row: %v`, err)
		}

		apiKeys = append(apiKeys, apiKey)
	}

	return apiKeys, nil
}

// CreateAPIKey inserts a new API key.
func (s *Storage) CreateAPIKey(userID int64, description string) (*model.APIKey, error) {
	query := `
		INSERT INTO api_keys
			(user_id, token, description)
		VALUES
			($1, $2, $3)
		RETURNING
			id, user_id, token, description, last_used_at, created_at
	`
	var apiKey model.APIKey
	err := s.db.QueryRow(
		query,
		userID,
		crypto.GenerateRandomStringHex(32),
		description,
	).Scan(
		&apiKey.ID,
		&apiKey.UserID,
		&apiKey.Token,
		&apiKey.Description,
		&apiKey.LastUsedAt,
		&apiKey.CreatedAt,
	)
	if err != nil {
		return nil, fmt.Errorf(`store: unable to create API Key: %v`, err)
	}

	return &apiKey, nil
}

// DeleteAPIKey deletes an API Key.
func (s *Storage) DeleteAPIKey(userID, keyID int64) error {
	result, err := s.db.Exec(`DELETE FROM api_keys WHERE id = $1 AND user_id = $2`, keyID, userID)
	if err != nil {
		return fmt.Errorf(`store: unable to delete this API Key: %v`, err)
	}

	count, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf(`store: unable to delete this API Key: %v`, err)
	}

	if count == 0 {
		return ErrAPIKeyNotFound
	}

	return nil
}
