package db

import (
	"context"
	"time"
)

func (r *PGRepository) ListBookingsByTenantBetween(ctx context.Context, tenantID string, from time.Time, to time.Time) ([]BookingRecord, error) {
	rows, err := r.pool.Query(ctx, `
		SELECT id, tenant_id, service_id, class_session_id, staff_id, location_id,
		       customer_name, customer_email, customer_phone, starts_at, ends_at, status, reference
		FROM bookings
		WHERE tenant_id = $1 AND starts_at < $3 AND ends_at > $2
		ORDER BY starts_at ASC
	`, tenantID, from, to)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var records []BookingRecord
	for rows.Next() {
		var record BookingRecord
		if err := rows.Scan(
			&record.ID,
			&record.TenantID,
			&record.ServiceID,
			&record.ClassSessionID,
			&record.StaffID,
			&record.LocationID,
			&record.CustomerName,
			&record.CustomerEmail,
			&record.CustomerPhone,
			&record.StartsAt,
			&record.EndsAt,
			&record.Status,
			&record.Reference,
		); err != nil {
			return nil, err
		}
		records = append(records, record)
	}
	return records, rows.Err()
}

func (r *PGRepository) CreateBooking(ctx context.Context, params CreateBookingParams) (CreatedBooking, error) {
	var created CreatedBooking
	err := r.pool.QueryRow(ctx, `
		INSERT INTO bookings (
			tenant_id, service_id, class_session_id, staff_id, location_id,
			booking_mode, customer_name, customer_email, customer_phone, starts_at, ends_at,
			status, reference, notes
		)
		VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)
		RETURNING id, reference, status
	`,
		params.TenantID,
		params.ServiceID,
		params.ClassSessionID,
		params.StaffID,
		params.LocationID,
		params.BookingMode,
		params.CustomerName,
		params.CustomerEmail,
		params.CustomerPhone,
		params.StartsAt,
		params.EndsAt,
		params.Status,
		params.Reference,
		params.Notes,
	).Scan(&created.ID, &created.Reference, &created.Status)
	return created, err
}

func (r *PGRepository) AppendWaitlistEntry(ctx context.Context, params WaitlistEntryParams) error {
	_, err := r.pool.Exec(ctx, `
		INSERT INTO waitlist_entries (tenant_id, class_session_id, customer_name, customer_email, position)
		VALUES ($1,$2,$3,$4,$5)
	`, params.TenantID, params.ClassSessionID, params.CustomerName, params.CustomerEmail, params.Position)
	return err
}

func (r *PGRepository) CreateReminderJob(ctx context.Context, params ReminderJobParams) error {
	_, err := r.pool.Exec(ctx, `
		INSERT INTO reminder_jobs (tenant_id, booking_id, channel, scheduled_for)
		VALUES ($1, $2, $3, $4)
	`, params.TenantID, params.BookingID, params.Channel, params.ScheduledFor)
	return err
}

func (r *PGRepository) ListDueReminderJobs(ctx context.Context, dueBefore time.Time, limit int) ([]ReminderJobRecord, error) {
	rows, err := r.pool.Query(ctx, `
		SELECT rj.id, rj.tenant_id, t.name, t.locale, t.timezone,
		       rj.booking_id, rj.channel, rj.scheduled_for,
		       b.customer_name, b.customer_email, b.reference, b.starts_at, rj.status
		FROM reminder_jobs rj
		INNER JOIN bookings b ON b.id = rj.booking_id
		INNER JOIN tenants t ON t.id = rj.tenant_id
		WHERE rj.status = 'pending' AND rj.scheduled_for <= $1
		ORDER BY rj.scheduled_for ASC
		LIMIT $2
	`, dueBefore, limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var records []ReminderJobRecord
	for rows.Next() {
		var record ReminderJobRecord
		if err := rows.Scan(
			&record.ID,
			&record.TenantID,
			&record.TenantName,
			&record.Locale,
			&record.Timezone,
			&record.BookingID,
			&record.Channel,
			&record.ScheduledFor,
			&record.CustomerName,
			&record.CustomerEmail,
			&record.Reference,
			&record.StartsAt,
			&record.Status,
		); err != nil {
			return nil, err
		}
		records = append(records, record)
	}
	return records, rows.Err()
}

func (r *PGRepository) MarkReminderJobDispatched(ctx context.Context, reminderJobID string, status string, dispatchedAt time.Time) error {
	_, err := r.pool.Exec(ctx, `
		UPDATE reminder_jobs
		SET status = $2, dispatched_at = $3
		WHERE id = $1
	`, reminderJobID, status, dispatchedAt)
	return err
}

func (r *PGRepository) CreateNotificationDeliveryLog(ctx context.Context, params NotificationDeliveryLogParams) error {
	_, err := r.pool.Exec(ctx, `
		INSERT INTO notification_delivery_logs (
			tenant_id, reminder_job_id, channel, provider, recipient,
			delivery_status, external_id, error_message
		)
		VALUES ($1, $2, $3, $4, $5, $6, $7, NULLIF($8, ''))
	`, params.TenantID, params.ReminderJobID, params.Channel, params.Provider, params.Recipient,
		params.Status, params.ExternalID, params.ErrorMessage)
	return err
}

func (r *PGRepository) GetDashboardMetrics(ctx context.Context, tenantID string, startsAt time.Time, endsAt time.Time) (DashboardMetrics, error) {
	var metrics DashboardMetrics
	err := r.pool.QueryRow(ctx, `
		SELECT
			COUNT(*) FILTER (WHERE starts_at >= $2 AND starts_at < $3) AS bookings_count,
			COUNT(*) FILTER (WHERE starts_at >= $2 AND starts_at < $3 AND status = 'cancelled') AS cancellations_count,
			COALESCE(
				ROUND(
					100.0 * COUNT(*) FILTER (WHERE starts_at >= $2 AND starts_at < $3 AND status = 'confirmed')
					/ NULLIF(COUNT(*) FILTER (WHERE starts_at >= $2 AND starts_at < $3), 0)
				),
				0
			)::integer AS utilization_percent
		FROM bookings
		WHERE tenant_id = $1
	`, tenantID, startsAt, endsAt).Scan(
		&metrics.BookingsCount,
		&metrics.CancellationsCount,
		&metrics.UtilizationPercent,
	)
	return metrics, err
}
