package db

import (
"context"
"time"
)

func (r *PGRepository) ListLocationsByTenant(ctx context.Context, tenantID string) ([]LocationRecord, error) {
	rows, err := r.pool.Query(ctx, `
		SELECT id, tenant_id, name, timezone, created_at
		FROM locations
		WHERE tenant_id = $1
		ORDER BY name
	`, tenantID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var records []LocationRecord
	for rows.Next() {
		var rec LocationRecord
		if err := rows.Scan(&rec.ID, &rec.TenantID, &rec.Name, &rec.Timezone, &rec.CreatedAt); err != nil {
			return nil, err
		}
		records = append(records, rec)
	}
	return records, rows.Err()
}

func (r *PGRepository) GetLocationByID(ctx context.Context, locationID string) (LocationRecord, error) {
	var rec LocationRecord
	err := r.pool.QueryRow(ctx, `
		SELECT id, tenant_id, name, timezone, created_at
		FROM locations
		WHERE id = $1
	`, locationID).Scan(&rec.ID, &rec.TenantID, &rec.Name, &rec.Timezone, &rec.CreatedAt)
	return rec, err
}

func (r *PGRepository) CreateLocation(ctx context.Context, params CreateLocationParams) (LocationRecord, error) {
	var rec LocationRecord
	err := r.pool.QueryRow(ctx, `
		INSERT INTO locations (tenant_id, name, timezone)
		VALUES ($1, $2, $3)
		RETURNING id, tenant_id, name, timezone, created_at
	`, params.TenantID, params.Name, params.Timezone).Scan(&rec.ID, &rec.TenantID, &rec.Name, &rec.Timezone, &rec.CreatedAt)
	return rec, err
}

func (r *PGRepository) UpdateLocation(ctx context.Context, locationID string, params UpdateLocationParams) (LocationRecord, error) {
	var rec LocationRecord
	err := r.pool.QueryRow(ctx, `
		UPDATE locations
		SET name = COALESCE($2, name),
		    timezone = COALESCE($3, timezone),
		    updated_at = now()
		WHERE id = $1
		RETURNING id, tenant_id, name, timezone, created_at
	`, locationID, params.Name, params.Timezone).Scan(&rec.ID, &rec.TenantID, &rec.Name, &rec.Timezone, &rec.CreatedAt)
	return rec, err
}

func (r *PGRepository) DeleteLocation(ctx context.Context, locationID string) error {
	_, err := r.pool.Exec(ctx, `DELETE FROM locations WHERE id = $1`, locationID)
	return err
}

func (r *PGRepository) ListBlockedDaysByTenant(ctx context.Context, tenantID string, from time.Time, to time.Time) ([]BlockedDayRecord, error) {
	rows, err := r.pool.Query(ctx, `
		SELECT id, tenant_id, staff_id, starts_at, ends_at, kind, reason, created_at
		FROM availability_exceptions
		WHERE tenant_id = $1 AND starts_at <= $3 AND ends_at >= $2
		ORDER BY starts_at
	`, tenantID, from, to)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var records []BlockedDayRecord
	for rows.Next() {
		var rec BlockedDayRecord
		if err := rows.Scan(&rec.ID, &rec.TenantID, &rec.StaffID, &rec.StartsAt, &rec.EndsAt, &rec.Kind, &rec.Reason, &rec.CreatedAt); err != nil {
			return nil, err
		}
		records = append(records, rec)
	}
	return records, rows.Err()
}

func (r *PGRepository) CreateBlockedDay(ctx context.Context, params CreateBlockedDayParams) (BlockedDayRecord, error) {
	var rec BlockedDayRecord
	err := r.pool.QueryRow(ctx, `
		INSERT INTO availability_exceptions (tenant_id, staff_id, starts_at, ends_at, kind, reason)
		VALUES ($1, $2, $3, $4, $5, $6)
		RETURNING id, tenant_id, staff_id, starts_at, ends_at, kind, reason, created_at
	`, params.TenantID, params.StaffID, params.StartsAt, params.EndsAt, params.Kind, params.Reason).Scan(&rec.ID, &rec.TenantID, &rec.StaffID, &rec.StartsAt, &rec.EndsAt, &rec.Kind, &rec.Reason, &rec.CreatedAt)
	return rec, err
}

func (r *PGRepository) UpdateBlockedDay(ctx context.Context, blockedDayID string, params UpdateBlockedDayParams) (BlockedDayRecord, error) {
	var rec BlockedDayRecord
	err := r.pool.QueryRow(ctx, `
		UPDATE availability_exceptions
		SET starts_at = COALESCE($2, starts_at),
		    ends_at = COALESCE($3, ends_at),
		    kind = COALESCE($4, kind),
		    reason = COALESCE($5, reason)
		WHERE id = $1
		RETURNING id, tenant_id, staff_id, starts_at, ends_at, kind, reason, created_at
	`, blockedDayID, params.StartsAt, params.EndsAt, params.Kind, params.Reason).Scan(&rec.ID, &rec.TenantID, &rec.StaffID, &rec.StartsAt, &rec.EndsAt, &rec.Kind, &rec.Reason, &rec.CreatedAt)
	return rec, err
}

func (r *PGRepository) DeleteBlockedDay(ctx context.Context, blockedDayID string) error {
	_, err := r.pool.Exec(ctx, `DELETE FROM availability_exceptions WHERE id = $1`, blockedDayID)
	return err
}

func (r *PGRepository) ListWorkingHoursByTenant(ctx context.Context, tenantID string) ([]WorkingHoursRecord, error) {
	rows, err := r.pool.Query(ctx, `
		SELECT tenant_id, staff_id, day_of_week, starts_local, ends_local
		FROM availability_rules
		WHERE tenant_id = $1 AND staff_id IS NULL
		ORDER BY day_of_week
	`, tenantID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var records []WorkingHoursRecord
	for rows.Next() {
		var rec WorkingHoursRecord
		if err := rows.Scan(&rec.TenantID, &rec.StaffID, &rec.DayOfWeek, &rec.StartsLocal, &rec.EndsLocal); err != nil {
			return nil, err
		}
		records = append(records, rec)
	}
	return records, rows.Err()
}

func (r *PGRepository) UpdateWorkingHours(ctx context.Context, tenantID string, dayOfWeek int, params UpdateWorkingHoursParams) error {
	_, err := r.pool.Exec(ctx, `
		UPDATE availability_rules
		SET starts_local = COALESCE($3, starts_local),
		    ends_local = COALESCE($4, ends_local)
		WHERE tenant_id = $1 AND day_of_week = $2 AND staff_id IS NULL
	`, tenantID, dayOfWeek, params.StartsLocal, params.EndsLocal)
	return err
}
