import { NextRequest, NextResponse } from 'next/server';
import { getTenantIdBySlug } from '@alga-psa/db';
import { getAvailableDates } from '@alga-psa/client-portal/services/availabilityService';
import { runWithTenant } from '@/lib/db';
import logger from '@alga-psa/core/logger';
import { formatInTimeZone } from 'date-fns-tz';
import { z } from 'zod';

// Tenant slug pattern: 12-char lowercase hex
const TENANT_SLUG_REGEX = /^[a-f0-9]{12}$/i;

// UUID pattern for validation
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

// Get valid IANA timezones for validation (with Node 18 fallback)
function getValidTimezones(): Set<string> | null {
  try {
    // Node 20+ has Intl.supportedValuesOf
    if (typeof Intl.supportedValuesOf === 'function') {
      return new Set(Intl.supportedValuesOf('timeZone'));
    }
  } catch {
    // Fall through to validation fallback
  }
  // For Node 18, we'll validate by attempting to construct a DateTimeFormat
  return null;
}

const validTimezones = getValidTimezones();

// Validate timezone by attempting to use it (works on all Node versions)
function isValidTimezone(tz: string): boolean {
  if (validTimezones) {
    return validTimezones.has(tz);
  }
  // Fallback: try to construct a DateTimeFormat with the timezone
  try {
    Intl.DateTimeFormat(undefined, { timeZone: tz });
    return true;
  } catch {
    return false;
  }
}

// Validation schema for query parameters
const availableDatesQuerySchema = z.object({
  tenant: z.string().min(1, 'Tenant is required'),
  service_id: z.string().uuid('Service ID must be a valid UUID'),
  start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Start date must be in YYYY-MM-DD format').optional(),
  end_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'End date must be in YYYY-MM-DD format').optional(),
  user_id: z.string().uuid('User ID must be a valid UUID').optional(),
  timezone: z.string()
    .refine(tz => isValidTimezone(tz), 'Invalid IANA timezone')
    .optional()
});

/**
 * GET /api/public/appointment-request/available-dates?tenant={tenant}&service_id={uuid}&start_date={YYYY-MM-DD}&end_date={YYYY-MM-DD}&user_id={uuid}&timezone={tz}
 *
 * Returns dates with availability for a specific service within a date range
 *
 * Query Parameters:
 * - tenant: Tenant slug (12-char hex) or tenant ID (UUID) - required
 * - service_id: Service UUID - required
 * - start_date: Start of date range in YYYY-MM-DD format (optional, defaults to today)
 * - end_date: End of date range in YYYY-MM-DD format (optional, defaults to 30 days from start)
 * - user_id: Optional technician UUID to filter availability by specific technician
 * - timezone: IANA timezone string (optional, e.g., 'America/New_York') - used for minimum notice calculation
 *
 * Response:
 * {
 *   "success": true,
 *   "dates": [
 *     {
 *       "date": "2025-01-15",
 *       "has_availability": true,
 *       "slot_count": 8
 *     }
 *   ]
 * }
 */
export async function GET(req: NextRequest) {
  try {
    const { searchParams } = new URL(req.url);

    // Extract and validate query parameters
    const queryParams = {
      tenant: searchParams.get('tenant'),
      service_id: searchParams.get('service_id'),
      start_date: searchParams.get('start_date') || undefined,
      end_date: searchParams.get('end_date') || undefined,
      user_id: searchParams.get('user_id') || undefined,
      timezone: searchParams.get('timezone') || undefined
    };

    let validatedParams;
    try {
      validatedParams = availableDatesQuerySchema.parse(queryParams);
    } catch (error) {
      if (error instanceof z.ZodError) {
        logger.warn('[available-dates] Validation error', {
          errors: error.errors
        });

        return NextResponse.json(
          {
            success: false,
            error: 'Invalid query parameters',
            details: error.errors
          },
          { status: 400 }
        );
      }
      throw error;
    }

    // Resolve tenant ID from slug if needed
    let tenantId = validatedParams.tenant;

    // If tenant looks like a slug (12-char hex), resolve it to UUID
    if (TENANT_SLUG_REGEX.test(validatedParams.tenant)) {
      const resolvedTenantId = await getTenantIdBySlug(validatedParams.tenant);
      if (!resolvedTenantId) {
        logger.warn('[available-dates] Invalid tenant slug', {
          slug: validatedParams.tenant
        });

        return NextResponse.json(
          {
            success: false,
            error: 'Invalid tenant identifier'
          },
          { status: 400 }
        );
      }
      tenantId = resolvedTenantId;
    } else if (!UUID_REGEX.test(validatedParams.tenant)) {
      // If not a slug, must be a valid UUID
      logger.warn('[available-dates] Invalid tenant format', {
        tenant: validatedParams.tenant
      });

      return NextResponse.json(
        {
          success: false,
          error: 'Tenant must be a valid slug or UUID'
        },
        { status: 400 }
      );
    }

    // "Today" is evaluated in the requester's timezone (when provided) so users
    // west of UTC aren't told their current date is in the past every evening.
    const now = new Date();
    const requesterTz = validatedParams.timezone || 'UTC';
    const todayLocal = formatInTimeZone(now, requesterTz, 'yyyy-MM-dd');

    const defaultStartDate = todayLocal;
    const defaultEndDate = formatInTimeZone(
      new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000),
      requesterTz,
      'yyyy-MM-dd'
    );

    const startDate = validatedParams.start_date || defaultStartDate;
    const endDate = validatedParams.end_date || defaultEndDate;

    // Validate start_date is not in the past (in the requester's timezone)
    const requestedStartDate = new Date(startDate + 'T00:00:00Z');
    if (startDate < todayLocal) {
      return NextResponse.json(
        {
          success: false,
          error: 'Start date cannot be in the past'
        },
        { status: 400 }
      );
    }

    // Validate end_date is after start_date
    const requestedEndDate = new Date(endDate + 'T00:00:00Z');
    if (requestedEndDate < requestedStartDate) {
      return NextResponse.json(
        {
          success: false,
          error: 'End date must be after start date'
        },
        { status: 400 }
      );
    }

    // Reject date range exceeding 90 days instead of silently truncating
    const maxEndDate = new Date(requestedStartDate.getTime() + 90 * 24 * 60 * 60 * 1000);
    if (requestedEndDate > maxEndDate) {
      return NextResponse.json(
        {
          success: false,
          error: 'Date range cannot exceed 90 days'
        },
        { status: 400 }
      );
    }

    // Execute within tenant context for RLS
    return await runWithTenant(tenantId, async () => {
      // Get available dates
      const dates = await getAvailableDates(
        tenantId,
        validatedParams.service_id,
        startDate,
        endDate,
        validatedParams.user_id,
        validatedParams.timezone
      );

      logger.info('[available-dates] Retrieved available dates', {
        tenantId,
        serviceId: validatedParams.service_id,
        startDate,
        endDate,
        userId: validatedParams.user_id,
        dateCount: dates.length,
        availableDateCount: dates.filter(d => d.has_availability).length
      });

      return NextResponse.json({
        success: true,
        dates
      });
    });

  } catch (error) {
    const err = error instanceof Error ? error : new Error(String(error));
    logger.error('[available-dates] Error retrieving dates', {
      message: err.message,
      stack: err.stack
    });

    return NextResponse.json(
      {
        success: false,
        error: 'Internal server error'
      },
      { status: 500 }
    );
  }
}

export const runtime = 'nodejs';
