package store

import (
	"context"
	"database/sql"
	"time"
)

type LoginAttempt struct {
	FailCount   int
	WindowStart time.Time
	LockedUntil *time.Time
}

func (s *Store) GetLoginAttempt(ctx context.Context, ip string) (*LoginAttempt, error) {
	var failCount int
	var windowStart string
	var lockedUntil sql.NullString
	err := s.db.QueryRowContext(ctx,
		`SELECT fail_count, window_start, locked_until FROM login_attempts WHERE ip = ?`, ip,
	).Scan(&failCount, &windowStart, &lockedUntil)
	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	out := &LoginAttempt{
		FailCount:   failCount,
		WindowStart: parseTime(windowStart),
	}
	if lockedUntil.Valid && lockedUntil.String != "" {
		t := parseTime(lockedUntil.String)
		out.LockedUntil = &t
	}
	return out, nil
}

func (s *Store) SetLoginAttempt(ctx context.Context, ip string, attempt LoginAttempt) error {
	var locked string
	if attempt.LockedUntil != nil {
		locked = attempt.LockedUntil.Format("2006-01-02 15:04:05")
	}
	_, err := s.db.ExecContext(ctx, `
		INSERT INTO login_attempts(ip, fail_count, window_start, locked_until)
		VALUES(?,?,?,?)
		ON CONFLICT(ip) DO UPDATE SET
			fail_count = excluded.fail_count,
			window_start = excluded.window_start,
			locked_until = excluded.locked_until
	`, ip, attempt.FailCount, attempt.WindowStart.Format("2006-01-02 15:04:05"), nullIfEmpty(locked))
	return err
}

func (s *Store) ClearLoginAttempt(ctx context.Context, ip string) error {
	_, err := s.db.ExecContext(ctx, `DELETE FROM login_attempts WHERE ip = ?`, ip)
	return err
}

func nullIfEmpty(s string) any {
	if s == "" {
		return nil
	}
	return s
}
