package db

import (
	"context"

	"github.com/jackc/pgx/v5"
)

func (r *PGRepository) GetTenantBySlug(ctx context.Context, slug string) (TenantRecord, error) {
	var record TenantRecord
	err := r.pool.QueryRow(ctx, `
		SELECT id, slug, name, preset, locale, timezone, plan_code, subscription_status, billing_provider, billing_customer_id, billing_subscription_id
		FROM tenants
		WHERE slug = $1
	`, slug).Scan(
		&record.ID, &record.Slug, &record.Name, &record.Preset, &record.Locale, &record.Timezone,
		&record.PlanCode, &record.SubscriptionStatus, &record.BillingProvider,
		&record.BillingCustomerID, &record.BillingSubscription,
	)
	return record, err
}

func (r *PGRepository) GetTenantByID(ctx context.Context, tenantID string) (TenantRecord, error) {
	var record TenantRecord
	err := r.pool.QueryRow(ctx, `
		SELECT id, slug, name, preset, locale, timezone, plan_code, subscription_status, billing_provider, billing_customer_id, billing_subscription_id
		FROM tenants
		WHERE id = $1
	`, tenantID).Scan(
		&record.ID, &record.Slug, &record.Name, &record.Preset, &record.Locale, &record.Timezone,
		&record.PlanCode, &record.SubscriptionStatus, &record.BillingProvider,
		&record.BillingCustomerID, &record.BillingSubscription,
	)
	return record, err
}

func (r *PGRepository) GetTenantByBillingCustomerID(ctx context.Context, customerID string) (TenantRecord, error) {
	var record TenantRecord
	err := r.pool.QueryRow(ctx, `
		SELECT id, slug, name, preset, locale, timezone, plan_code, subscription_status, billing_provider, billing_customer_id, billing_subscription_id
		FROM tenants
		WHERE billing_customer_id = $1
	`, customerID).Scan(
		&record.ID, &record.Slug, &record.Name, &record.Preset, &record.Locale, &record.Timezone,
		&record.PlanCode, &record.SubscriptionStatus, &record.BillingProvider,
		&record.BillingCustomerID, &record.BillingSubscription,
	)
	return record, err
}

func (r *PGRepository) EnsureUserIdentity(ctx context.Context, subject string, email string, displayName string) error {
	_, err := r.pool.Exec(ctx, `
		INSERT INTO users (id, neon_subject, email, display_name)
		VALUES (gen_random_uuid(), $1, COALESCE(NULLIF($2, ''), $1 || '@users.bookra.invalid'), NULLIF($3, ''))
		ON CONFLICT (neon_subject) DO UPDATE SET email = EXCLUDED.email, display_name = COALESCE(NULLIF($3, ''), users.display_name)
	`, subject, email, displayName)
	return err
}

func (r *PGRepository) CreateTenantForUser(ctx context.Context, params CreateTenantForUserParams) (TenantMembershipRecord, error) {
	tx, err := r.pool.BeginTx(ctx, pgx.TxOptions{})
	if err != nil {
		return TenantMembershipRecord{}, err
	}
	defer func() { _ = tx.Rollback(ctx) }()

	var tenantID string
	err = tx.QueryRow(ctx, `
		INSERT INTO tenants (id, slug, name, preset, locale, timezone, plan_code, subscription_status, billing_provider)
		VALUES (gen_random_uuid(), $1, $2, $3, $4, $5, 'starter', 'inactive', '')
		RETURNING id
	`, params.Slug, params.Name, params.Preset, params.Locale, params.Timezone).Scan(&tenantID)
	if err != nil {
		return TenantMembershipRecord{}, err
	}

	_, err = tx.Exec(ctx, `
		INSERT INTO brand_profiles (tenant_id, name, site_url, logo_url, primary_color)
		VALUES ($1, $2, NULLIF($3,''), NULLIF($4,''), NULLIF($5,''))
	`, tenantID, params.BrandName, params.SiteURL, params.LogoURL, params.PrimaryColor)
	if err != nil {
		return TenantMembershipRecord{}, err
	}

	locationID := ""
	if params.LocationName != "" {
		err = tx.QueryRow(ctx, `
			INSERT INTO locations (id, tenant_id, name, timezone)
			VALUES (gen_random_uuid(), $1, $2, $3)
			RETURNING id
		`, tenantID, params.LocationName, params.Timezone).Scan(&locationID)
		if err != nil {
			return TenantMembershipRecord{}, err
		}
	}

	if params.ServiceName != "" && params.DurationMinutes > 0 {
		_, err = tx.Exec(ctx, `
			INSERT INTO services (id, tenant_id, name, duration_minutes, buffer_before_minutes, buffer_after_minutes, price_cents)
			VALUES (gen_random_uuid(), $1, $2, $3, $4, $5, 0)
		`, tenantID, params.ServiceName, params.DurationMinutes, params.BufferBeforeMinutes, params.BufferAfterMinutes)
		if err != nil {
			return TenantMembershipRecord{}, err
		}
	}

	for _, block := range params.AvailabilityBlocks {
		_, err = tx.Exec(ctx, `
			INSERT INTO availability_rules (id, tenant_id, day_of_week, starts_local, ends_local)
			VALUES (gen_random_uuid(), $1, $2, $3, $4)
		`, tenantID, block.DayOfWeek, block.StartsLocal, block.EndsLocal)
		if err != nil {
			return TenantMembershipRecord{}, err
		}
	}

	for _, invite := range params.TeamInvites {
		_, _ = tx.Exec(ctx, `
			INSERT INTO team_invites (tenant_id, email, role, expires_at)
			VALUES ($1, $2, $3, now() + interval '7 days')
		`, tenantID, invite.Email, invite.Role)
	}

	_, err = tx.Exec(ctx, `
		INSERT INTO users (id, neon_subject, email, display_name)
		VALUES (gen_random_uuid(), $1, $2, $3)
		ON CONFLICT (neon_subject) DO NOTHING
	`, params.Subject, params.Subject+"@users.bookra.invalid", "")
	if err != nil {
		return TenantMembershipRecord{}, err
	}

	var userID string
	err = tx.QueryRow(ctx, `
		INSERT INTO tenant_memberships (id, tenant_id, user_neon_subject, role, joined_at)
		SELECT gen_random_uuid(), $1, u.neon_subject, 'owner', now()
		FROM users u WHERE u.neon_subject = $2
		ON CONFLICT (tenant_id, user_neon_subject) DO UPDATE SET role = 'owner'
		RETURNING id
	`, tenantID, params.Subject).Scan(&userID)
	if err != nil {
		return TenantMembershipRecord{}, err
	}

	err = tx.Commit(ctx)
	if err != nil {
		return TenantMembershipRecord{}, err
	}

	return TenantMembershipRecord{
		Tenant: TenantRecord{
			ID:       tenantID,
			Slug:     params.Slug,
			Name:     params.Name,
			Preset:   params.Preset,
			Locale:   params.Locale,
			Timezone: params.Timezone,
		},
		UserID: userID,
		Role:   "owner",
	}, nil
}

func (r *PGRepository) GetBrandProfile(ctx context.Context, tenantID string) (BrandProfileRecord, error) {
	var record BrandProfileRecord
	err := r.pool.QueryRow(ctx, `
		SELECT tenant_id, name, COALESCE(site_url, ''), COALESCE(logo_url, ''), COALESCE(primary_color, ''), COALESCE(umami_site_id, '')
		FROM brand_profiles WHERE tenant_id = $1
	`, tenantID).Scan(&record.TenantID, &record.Name, &record.SiteURL, &record.LogoURL, &record.PrimaryColor, &record.UmamiSiteID)
	return record, err
}

func (r *PGRepository) GetTenantMembershipByUserID(ctx context.Context, userID string) (TenantMembershipRecord, error) {
	var record TenantMembershipRecord
	err := r.pool.QueryRow(ctx, `
		SELECT t.id, t.slug, t.name, t.preset, t.locale, t.timezone, t.plan_code, t.subscription_status,
		       t.billing_provider, t.billing_customer_id, t.billing_subscription_id,
		       tm.user_neon_subject, tm.role
		FROM tenant_memberships tm
		JOIN tenants t ON t.id = tm.tenant_id
		JOIN users u ON u.neon_subject = tm.user_neon_subject
		WHERE u.id = $1
		ORDER BY tm.joined_at DESC
		LIMIT 1
	`, userID).Scan(
		&record.Tenant.ID, &record.Tenant.Slug, &record.Tenant.Name, &record.Tenant.Preset,
		&record.Tenant.Locale, &record.Tenant.Timezone, &record.Tenant.PlanCode, &record.Tenant.SubscriptionStatus,
		&record.Tenant.BillingProvider, &record.Tenant.BillingCustomerID, &record.Tenant.BillingSubscription,
		&record.UserID, &record.Role,
	)
	return record, err
}

func (r *PGRepository) UpdateTenantBillingCustomerID(ctx context.Context, tenantID string, customerID string) error {
	_, err := r.pool.Exec(ctx, `
		UPDATE tenants
		SET billing_provider = 'paddle', billing_customer_id = $2, updated_at = now()
		WHERE id = $1
	`, tenantID, customerID)
	return err
}

func (r *PGRepository) UpdateTenantBillingState(ctx context.Context, tenantID string, planCode string, subscriptionStatus string, subscriptionID string) error {
	_, err := r.pool.Exec(ctx, `
		UPDATE tenants
		SET billing_provider = 'paddle', plan_code = $2, subscription_status = $3, billing_subscription_id = $4, updated_at = now()
		WHERE id = $1
	`, tenantID, planCode, subscriptionStatus, subscriptionID)
	return err
}
