-- Fix Malformed Time Entries in OptionsFlow_monthly
-- This script identifies and corrects time entries that are missing the hour component
-- Examples: "34 AM" should be "11:34 AM" or "9:34 AM"

-- Step 1: Identify malformed time entries
-- Times that match pattern: just digits followed by AM/PM (no colon, no hour)
-- Examples: "34 AM", "45 PM", "12 AM" (without hour)

SELECT 
  COUNT(*) as malformed_count,
  COUNT(DISTINCT "CreatedDate") as affected_dates
FROM "OptionsFlow_monthly"
WHERE "CreatedTime" IS NOT NULL
  AND "CreatedTime"::text ~ '^\d{1,2}\s*(AM|PM)$'  -- Just number + AM/PM, no colon
  AND "CreatedTime"::text !~ '^\d{1,2}:\d{2}'     -- No colon present
  AND "CreatedTime"::text !~ '^\d{1,2}:\d{2}:\d{2}'; -- No full time format

-- Step 2: Show sample malformed entries
SELECT 
  "CreatedDate",
  "CreatedTime",
  "Symbol",
  COUNT(*) as occurrences
FROM "OptionsFlow_monthly"
WHERE "CreatedTime" IS NOT NULL
  AND "CreatedTime"::text ~ '^\d{1,2}\s*(AM|PM)$'
  AND "CreatedTime"::text !~ '^\d{1,2}:\d{2}'
GROUP BY "CreatedDate", "CreatedTime", "Symbol"
ORDER BY "CreatedDate" DESC, occurrences DESC
LIMIT 20;

-- Step 3: Fix malformed times by inferring hour from context
-- Strategy: Look at surrounding records on the same date to infer the hour
-- If no context available, use a default based on the minute value:
--   - Minutes 0-30: likely morning (9-11 AM) or afternoon (1-3 PM)
--   - Minutes 31-59: likely late morning (10-11 AM) or afternoon (2-4 PM)

-- Create a temporary table with corrected times
CREATE TEMP TABLE time_fixes AS
WITH malformed AS (
  SELECT 
    ctid,  -- Physical row identifier
    "CreatedDate",
    "CreatedTime",
    "Symbol",
    -- Extract the minute value
    CASE 
      WHEN "CreatedTime"::text ~ '^(\d{1,2})\s*(AM|PM)$' 
      THEN (REGEXP_REPLACE("CreatedTime"::text, '^(\d{1,2})\s*(AM|PM)$', '\1'))::int
      ELSE NULL
    END AS minute_only,
    CASE 
      WHEN "CreatedTime"::text ~ '^(\d{1,2})\s*(AM|PM)$' 
      THEN REGEXP_REPLACE("CreatedTime"::text, '^(\d{1,2})\s*(AM|PM)$', '\2')
      ELSE NULL
    END AS am_pm
  FROM "OptionsFlow_monthly"
  WHERE "CreatedTime" IS NOT NULL
    AND "CreatedTime"::text ~ '^\d{1,2}\s*(AM|PM)$'
    AND "CreatedTime"::text !~ '^\d{1,2}:\d{2}'
),
context_hours AS (
  -- Try to infer hour from nearby records on the same date
  SELECT DISTINCT ON (m.ctid)
    m.ctid,
    m."CreatedDate",
    m."CreatedTime",
    m.minute_only,
    m.am_pm,
    -- Look for nearby records with proper time format
    CASE 
      WHEN nearby."CreatedTime" IS NOT NULL 
        AND nearby."CreatedTime"::text ~ '^\d{1,2}:\d{2}'
      THEN 
        CASE 
          WHEN nearby."CreatedTime"::text ~ '^(\d{1,2}):\d{2}\s*(AM|PM)$'
          THEN (REGEXP_REPLACE(nearby."CreatedTime"::text, '^(\d{1,2}):\d{2}\s*(AM|PM)$', '\1'))::int
          ELSE NULL
        END
      ELSE NULL
    END AS inferred_hour
  FROM malformed m
  LEFT JOIN LATERAL (
    SELECT "CreatedTime"
    FROM "OptionsFlow_monthly" n
    WHERE n."CreatedDate" = m."CreatedDate"
      AND n."CreatedTime" IS NOT NULL
      AND n."CreatedTime"::text ~ '^\d{1,2}:\d{2}'
      AND n."Symbol" = m."Symbol"  -- Same symbol for better context
    ORDER BY ABS(
      CASE 
        WHEN n."CreatedTime"::text ~ '^(\d{1,2}):(\d{2})\s*(AM|PM)$'
        THEN (REGEXP_REPLACE(n."CreatedTime"::text, '^(\d{1,2}):(\d{2})\s*(AM|PM)$', '\2'))::int
        ELSE 999
      END - m.minute_only
    )
    LIMIT 1
  ) nearby ON TRUE
),
fixed_times AS (
  SELECT 
    tf.ctid,
    tf."CreatedDate",
    tf."CreatedTime" as original_time,
    tf.minute_only,
    tf.am_pm,
    COALESCE(tf.inferred_hour, 
      -- Default hour based on minute and AM/PM
      CASE 
        WHEN tf.am_pm = 'AM' THEN
          CASE 
            WHEN tf.minute_only <= 30 THEN 9  -- Early morning: 9 AM
            ELSE 10  -- Late morning: 10 AM
          END
        ELSE  -- PM
          CASE 
            WHEN tf.minute_only <= 30 THEN 1  -- Early afternoon: 1 PM
            ELSE 2  -- Late afternoon: 2 PM
          END
      END
    ) as fixed_hour,
    -- Build the corrected time string
    CASE 
      WHEN tf.inferred_hour IS NOT NULL THEN
        LPAD(tf.inferred_hour::text, 2, '0') || ':' || LPAD(tf.minute_only::text, 2, '0') || ':00 ' || tf.am_pm
      ELSE
        LPAD(
          CASE 
            WHEN tf.am_pm = 'AM' THEN
              CASE 
                WHEN tf.minute_only <= 30 THEN 9
                ELSE 10
              END
            ELSE
              CASE 
                WHEN tf.minute_only <= 30 THEN 1
                ELSE 2
              END
          END::text, 
          2, '0'
        ) || ':' || LPAD(tf.minute_only::text, 2, '0') || ':00 ' || tf.am_pm
    END as fixed_time
  FROM context_hours tf
)
SELECT * FROM fixed_times;

-- Step 4: Preview the fixes (run this first to verify)
SELECT 
  "CreatedDate",
  original_time,
  fixed_time,
  COUNT(*) as count
FROM time_fixes
GROUP BY "CreatedDate", original_time, fixed_time
ORDER BY "CreatedDate" DESC, count DESC
LIMIT 50;

-- Step 5: Apply the fixes (UNCOMMENT TO EXECUTE)
-- WARNING: This will update the database. Review the preview above first!

/*
UPDATE "OptionsFlow_monthly" ofm
SET "CreatedTime" = tf.fixed_time
FROM time_fixes tf
WHERE ofm.ctid = tf.ctid
  AND ofm."CreatedTime" = tf.original_time;
*/

-- Step 6: Verify the fixes
SELECT 
  COUNT(*) as remaining_malformed
FROM "OptionsFlow_monthly"
WHERE "CreatedTime" IS NOT NULL
  AND "CreatedTime"::text ~ '^\d{1,2}\s*(AM|PM)$'
  AND "CreatedTime"::text !~ '^\d{1,2}:\d{2}';

-- Alternative: More aggressive fix for times like ":34 AM" (missing hour, has colon)
-- This handles cases where the hour was completely missing
UPDATE "OptionsFlow_monthly"
SET "CreatedTime" = 
  CASE 
    -- Pattern: ":34 AM" -> "9:34 AM" (default to 9 AM for morning)
    WHEN "CreatedTime"::text ~ '^:\d{1,2}\s*AM$' THEN
      '9:' || LPAD(REGEXP_REPLACE("CreatedTime"::text, '^:(\d{1,2})\s*AM$', '\1'), 2, '0') || ':00 AM'
    -- Pattern: ":34 PM" -> "1:34 PM" (default to 1 PM for afternoon)
    WHEN "CreatedTime"::text ~ '^:\d{1,2}\s*PM$' THEN
      '1:' || LPAD(REGEXP_REPLACE("CreatedTime"::text, '^:(\d{1,2})\s*PM$', '\1'), 2, '0') || ':00 PM'
    ELSE "CreatedTime"
  END
WHERE "CreatedTime" IS NOT NULL
  AND (
    "CreatedTime"::text ~ '^:\d{1,2}\s*AM$' 
    OR "CreatedTime"::text ~ '^:\d{1,2}\s*PM$'
  );

-- Final verification query
SELECT 
  "CreatedDate",
  "CreatedTime",
  COUNT(*) as count
FROM "OptionsFlow_monthly"
WHERE "CreatedTime" IS NOT NULL
  AND (
    "CreatedTime"::text ~ '^\d{1,2}\s*(AM|PM)$'  -- Just number + AM/PM
    OR "CreatedTime"::text ~ '^:\d{1,2}\s*(AM|PM)$'  -- Colon + number + AM/PM
  )
GROUP BY "CreatedDate", "CreatedTime"
ORDER BY count DESC
LIMIT 20;

