package sqlite

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

	"switchboard-core/models"
	"switchboard-core/store"
)

type NotificationStore struct{}

func NewNotificationStore() *NotificationStore { return &NotificationStore{} }

func (s *NotificationStore) Create(ctx context.Context, n *models.Notification) error {
	if n.ID == "" {
		n.ID = store.NewID()
	}
	now := time.Now().UTC().Format(timeFmt)

	_, err := DB.ExecContext(ctx, `
		INSERT INTO notifications (id, user_id, type, title, body, resource_type, resource_id, is_read, created_at)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		n.ID, n.UserID, n.Type, n.Title, n.Body,
		nullStr(n.ResourceType), nullStr(n.ResourceID), boolToInt(n.IsRead), now,
	)
	if err != nil {
		return err
	}
	n.CreatedAt, _ = time.Parse(timeFmt, now)
	return nil
}

func (s *NotificationStore) ListByUser(ctx context.Context, userID string, limit, offset int, unreadOnly bool) ([]models.Notification, int, error) {
	if limit <= 0 {
		limit = 20
	}

	// Count
	countQ := `SELECT COUNT(*) FROM notifications WHERE user_id = ?`
	countArgs := []interface{}{userID}
	if unreadOnly {
		countQ += ` AND is_read = 0`
	}

	var total int
	if err := DB.QueryRowContext(ctx, countQ, countArgs...).Scan(&total); err != nil {
		return nil, 0, err
	}

	// Fetch
	q := `SELECT id, user_id, type, title, body, resource_type, resource_id, is_read, created_at
		FROM notifications WHERE user_id = ?`
	if unreadOnly {
		q += ` AND is_read = 0`
	}
	q += ` ORDER BY created_at DESC LIMIT ? OFFSET ?`

	rows, err := DB.QueryContext(ctx, q, userID, limit, offset)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()

	var items []models.Notification
	for rows.Next() {
		var n models.Notification
		var resType, resID sql.NullString
		var isRead int
		if err := rows.Scan(
			&n.ID, &n.UserID, &n.Type, &n.Title, &n.Body,
			&resType, &resID, &isRead, st(&n.CreatedAt),
		); err != nil {
			return nil, 0, err
		}
		n.ResourceType = NullableString(resType)
		n.ResourceID = NullableString(resID)
		n.IsRead = isRead != 0
		items = append(items, n)
	}
	if items == nil {
		items = []models.Notification{}
	}
	return items, total, rows.Err()
}

func (s *NotificationStore) MarkRead(ctx context.Context, id, userID string) error {
	res, err := DB.ExecContext(ctx,
		`UPDATE notifications SET is_read = 1 WHERE id = ? AND user_id = ?`,
		id, userID)
	if err != nil {
		return err
	}
	n, _ := res.RowsAffected()
	if n == 0 {
		return sql.ErrNoRows
	}
	return nil
}

func (s *NotificationStore) MarkAllRead(ctx context.Context, userID string) error {
	_, err := DB.ExecContext(ctx,
		`UPDATE notifications SET is_read = 1 WHERE user_id = ? AND is_read = 0`,
		userID)
	return err
}

func (s *NotificationStore) Delete(ctx context.Context, id, userID string) error {
	res, err := DB.ExecContext(ctx,
		`DELETE FROM notifications WHERE id = ? AND user_id = ?`, id, userID)
	if err != nil {
		return err
	}
	n, _ := res.RowsAffected()
	if n == 0 {
		return sql.ErrNoRows
	}
	return nil
}

func (s *NotificationStore) UnreadCount(ctx context.Context, userID string) (int, error) {
	var count int
	err := DB.QueryRowContext(ctx,
		`SELECT COUNT(*) FROM notifications WHERE user_id = ? AND is_read = 0`,
		userID).Scan(&count)
	return count, err
}

func (s *NotificationStore) DeleteOlderThan(ctx context.Context, before time.Time) (int64, error) {
	res, err := DB.ExecContext(ctx,
		`DELETE FROM notifications WHERE created_at < ?`,
		before.UTC().Format(timeFmt))
	if err != nil {
		return 0, err
	}
	return res.RowsAffected()
}

// nullStr returns sql.NullString for optional string fields.
func nullStr(s string) sql.NullString {
	if s == "" {
		return sql.NullString{}
	}
	return sql.NullString{String: s, Valid: true}
}
