package services

import (
	"context"
	"database/sql"

	"maintainarr/internal/models"
)

type Repository struct {
	db *sql.DB
}

func NewRepository(database *sql.DB) *Repository {
	return &Repository{db: database}
}

func (r *Repository) DB() *sql.DB {
	return r.db
}

func (r *Repository) EnsureOrganization(ctx context.Context, name, theme string) (models.Organization, error) {
	var organization models.Organization
	err := r.db.QueryRowContext(ctx, `
		SELECT id, name, theme, theme_mode, auto_update_window_start, auto_update_window_end, auto_update_days, created_at
		FROM organizations
		LIMIT 1
	`).Scan(&organization.ID, &organization.Name, &organization.Theme, &organization.ThemeMode, &organization.AutoUpdateWindowStart, &organization.AutoUpdateWindowEnd, &organization.AutoUpdateDays, &organization.CreatedAt)
	if err == nil {
		return organization, nil
	}
	if err != sql.ErrNoRows {
		return organization, err
	}

	result, err := r.db.ExecContext(ctx, `INSERT INTO organizations (name, theme, theme_mode) VALUES (?, ?, 'dark')`, name, theme)
	if err != nil {
		return organization, err
	}
	organization.ID, _ = result.LastInsertId()
	organization.Name = name
	organization.Theme = theme
	organization.ThemeMode = "dark"
	return organization, nil
}

func (r *Repository) GetOrganization(ctx context.Context) (models.Organization, error) {
	var organization models.Organization
	err := r.db.QueryRowContext(ctx, `
		SELECT id, name, theme, theme_mode, auto_update_window_start, auto_update_window_end, auto_update_days, created_at
		FROM organizations
		LIMIT 1
	`).Scan(&organization.ID, &organization.Name, &organization.Theme, &organization.ThemeMode, &organization.AutoUpdateWindowStart, &organization.AutoUpdateWindowEnd, &organization.AutoUpdateDays, &organization.CreatedAt)
	return organization, err
}

func (r *Repository) UpdateOrganizationTheme(ctx context.Context, theme, themeMode string) error {
	_, err := r.db.ExecContext(ctx, `
		UPDATE organizations
		SET theme = ?, theme_mode = ?
		WHERE id = (SELECT id FROM organizations LIMIT 1)
	`, theme, themeMode)
	return err
}

func (r *Repository) UpdateOrganizationAutoUpdateWindow(ctx context.Context, start, end, days string) error {
	_, err := r.db.ExecContext(ctx, `
		UPDATE organizations
		SET auto_update_window_start = ?, auto_update_window_end = ?, auto_update_days = ?
		WHERE id = (SELECT id FROM organizations LIMIT 1)
	`, start, end, days)
	return err
}

func (r *Repository) CountUsers(ctx context.Context) (int, error) {
	var count int
	err := r.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM users`).Scan(&count)
	return count, err
}

func (r *Repository) CreateUser(ctx context.Context, user *models.User) error {
	result, err := r.db.ExecContext(ctx, `
		INSERT INTO users (organization_id, name, email, password_hash, role, otp_secret, otp_enabled, theme_mode)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?)
	`, user.Organization, user.Name, user.Email, user.PasswordHash, user.Role, user.OTPSecret, user.OTPEnabled, user.ThemeMode)
	if err != nil {
		return err
	}

	user.ID, _ = result.LastInsertId()
	return nil
}

func (r *Repository) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
	user := &models.User{}
	err := r.db.QueryRowContext(ctx, `
		SELECT id, organization_id, name, email, password_hash, role, otp_secret, otp_enabled, theme_mode, created_at
		FROM users
		WHERE email = ?
	`, email).Scan(
		&user.ID, &user.Organization, &user.Name, &user.Email, &user.PasswordHash, &user.Role,
		&user.OTPSecret, &user.OTPEnabled, &user.ThemeMode, &user.CreatedAt,
	)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func (r *Repository) GetUserByID(ctx context.Context, id int64) (*models.User, error) {
	user := &models.User{}
	err := r.db.QueryRowContext(ctx, `
		SELECT id, organization_id, name, email, password_hash, role, otp_secret, otp_enabled, theme_mode, created_at
		FROM users
		WHERE id = ?
	`, id).Scan(
		&user.ID, &user.Organization, &user.Name, &user.Email, &user.PasswordHash, &user.Role,
		&user.OTPSecret, &user.OTPEnabled, &user.ThemeMode, &user.CreatedAt,
	)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func (r *Repository) EnableUserOTP(ctx context.Context, id int64) error {
	_, err := r.db.ExecContext(ctx, `UPDATE users SET otp_enabled = 1 WHERE id = ?`, id)
	return err
}

func (r *Repository) UpdateUserPassword(ctx context.Context, id int64, passwordHash string) error {
	_, err := r.db.ExecContext(ctx, `UPDATE users SET password_hash = ? WHERE id = ?`, passwordHash, id)
	return err
}

func (r *Repository) UpdateUserThemeMode(ctx context.Context, id int64, themeMode string) error {
	_, err := r.db.ExecContext(ctx, `UPDATE users SET theme_mode = ? WHERE id = ?`, themeMode, id)
	return err
}

func (r *Repository) UpdateUserOTP(ctx context.Context, id int64, secret string, enabled bool) error {
	_, err := r.db.ExecContext(ctx, `UPDATE users SET otp_secret = ?, otp_enabled = ? WHERE id = ?`, secret, enabled, id)
	return err
}

func (r *Repository) EnsurePrimaryAdmin(ctx context.Context) error {
	var adminCount int
	if err := r.db.QueryRowContext(ctx, `
		SELECT COUNT(*)
		FROM users
		WHERE role = ?
	`, models.RoleAdmin).Scan(&adminCount); err != nil {
		return err
	}
	if adminCount > 0 {
		return nil
	}

	var userID int64
	err := r.db.QueryRowContext(ctx, `
		SELECT id
		FROM users
		ORDER BY created_at ASC, id ASC
		LIMIT 1
	`).Scan(&userID)
	if err == sql.ErrNoRows {
		return nil
	}
	if err != nil {
		return err
	}

	_, err = r.db.ExecContext(ctx, `
		UPDATE users
		SET role = ?
		WHERE id = ?
	`, models.RoleAdmin, userID)
	return err
}

func (r *Repository) ListGroups(ctx context.Context, orgID int64) ([]models.VMGroup, error) {
	rows, err := r.db.QueryContext(ctx, `
		SELECT id, organization_id, name, description, color_token, icon, created_at
		FROM vm_groups
		WHERE organization_id = ?
		ORDER BY name ASC
	`, orgID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var groups []models.VMGroup
	for rows.Next() {
		var group models.VMGroup
		if err := rows.Scan(&group.ID, &group.OrganizationID, &group.Name, &group.Description, &group.ColorToken, &group.Icon, &group.CreatedAt); err != nil {
			return nil, err
		}
		groups = append(groups, group)
	}

	return groups, rows.Err()
}

func (r *Repository) CreateGroup(ctx context.Context, group *models.VMGroup) error {
	result, err := r.db.ExecContext(ctx, `
		INSERT INTO vm_groups (organization_id, name, description, color_token, icon)
		VALUES (?, ?, ?, ?, ?)
	`, group.OrganizationID, group.Name, group.Description, group.ColorToken, group.Icon)
	if err != nil {
		return err
	}

	group.ID, _ = result.LastInsertId()
	return nil
}

func (r *Repository) UpdateGroup(ctx context.Context, group *models.VMGroup) error {
	_, err := r.db.ExecContext(ctx, `
		UPDATE vm_groups
		SET name = ?, description = ?, color_token = ?, icon = ?
		WHERE id = ? AND organization_id = ?
	`, group.Name, group.Description, group.ColorToken, group.Icon, group.ID, group.OrganizationID)
	return err
}

func (r *Repository) ListTags(ctx context.Context, orgID int64) ([]string, error) {
	rows, err := r.db.QueryContext(ctx, `
		SELECT DISTINCT tag
		FROM nodes
		WHERE organization_id = ? AND tag <> ''
		ORDER BY tag ASC
	`, orgID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var tags []string
	for rows.Next() {
		var tag string
		if err := rows.Scan(&tag); err != nil {
			return nil, err
		}
		tags = append(tags, tag)
	}

	return tags, rows.Err()
}

func (r *Repository) SeedDefaults(ctx context.Context, orgID int64) error {
	_ = ctx
	_ = orgID
	return nil
}
