#!/usr/bin/env python3
"""Offline editorial check of explicitly synthetic getShipment planned intervals.
No API calls; no inference about lateness, acceptance or sanctions.
Run from repository root: python3 data/editorial/ai-market-fbs-shipment-timezone-boundary/normalize.py
"""
import json
import re
from collections import Counter
from datetime import datetime, timezone
from pathlib import Path

ROOT = Path(__file__).resolve().parent
STAMP = re.compile(r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?\Z')
OFFSET = re.compile(r'(?:Z|[+-]\d{2}:\d{2})\Z')
FIELDS = ('planIntervalFrom', 'planIntervalTo')


def normalize(source):
    result = {'input': dict(source), 'start_utc': None, 'end_utc': None,
              'duration_seconds': None, 'lateness': 'not_assessed'}
    def unknown(reason):
        return dict(result, status='unknown', reason=reason)
    if not any(field in source for field in FIELDS):
        return unknown('not_getShipment_plan_interval')
    if any(source.get(field) in (None, '') for field in FIELDS):
        return unknown('missing_plan_interval_field')
    times = []
    for field, output in zip(FIELDS, ('start_utc', 'end_utc')):
        value = source[field]
        if not isinstance(value, str):
            return unknown('invalid_timestamp_type')
        if re.fullmatch(r'\d{4}-\d{2}-\d{2}', value):
            return unknown('date_not_timestamp')
        if not STAMP.fullmatch(value):
            return unknown('invalid_timestamp')
        if not OFFSET.search(value) or value.endswith('-00:00'):
            return unknown('missing_utc_offset')
        try:
            parsed = datetime.fromisoformat(value.replace('Z', '+00:00'))
        except ValueError:
            return unknown('invalid_timestamp')
        utc = parsed.astimezone(timezone.utc)
        times.append(utc)
        result[output] = utc.isoformat()
    result['duration_seconds'] = (times[1] - times[0]).total_seconds()
    if result['duration_seconds'].is_integer():
        result['duration_seconds'] = int(result['duration_seconds'])
    if times[1] < times[0]:
        return dict(result, status='conflict', reason='end_before_start')
    return dict(result, status='known')


def main():
    fixtures = json.loads((ROOT / 'cases.json').read_text())
    rows = []
    for case in fixtures['cases']:
        actual = normalize(case['input'])
        mismatch = {key: {'expected': val, 'actual': actual.get(key)}
                    for key, val in case['expected'].items() if actual.get(key) != val}
        rows.append(dict(id=case['id'], **actual, expected_match=not mismatch, mismatches=mismatch))
    assert len(rows) == 9
    assert all(row['expected_match'] for row in rows), rows
    report = {'kind': fixtures['kind'], 'contract': fixtures['contract'],
              'editorial_policy': fixtures['editorial_policy'], 'cases': rows,
              'checked': len(rows), 'passed': sum(row['expected_match'] for row in rows),
              'status_counts': dict(Counter(row['status'] for row in rows)),
              'not_checked': ['seller API runtime', 'AI extraction accuracy', 'real shipments',
                              'lateness and sanctions', 'any timestamp formats outside this parser']}
    (ROOT / 'normalized.json').write_text(json.dumps(report, ensure_ascii=False, indent=2) + '\n')
    header = '# Девять синтетических интервалов FBS\n\nНе реальные заказы и не результаты теста ИИ. unknown/conflict — редакционные метки, не статусы Маркета.\n\n'
    table = '| Кейс | Исходное начало | Исходный конец | Начало UTC | Конец UTC | Секунды | Результат |\n|---|---|---|---|---|---|---|\n'
    for row in rows:
        source = row['input']
        values = [row['id'], source.get(FIELDS[0], 'нет'), source.get(FIELDS[1], 'нет'),
                  row['start_utc'] or 'неизвестно', row['end_utc'] or 'неизвестно',
                  str(row['duration_seconds']) if row['duration_seconds'] is not None else 'неизвестно',
                  row['status'] + (': ' + row['reason'] if 'reason' in row else '')]
        table += '| ' + ' | '.join(values) + ' |\n'
    (ROOT / 'table.md').write_text(header + table + '\nОтрицательная длительность — конфликт входа; границы не переставляем. Полный исходный объект, включая посторонние поля, сохранён в normalized.json.\n')
    print(json.dumps({k: report[k] for k in ('checked', 'passed', 'status_counts')}, ensure_ascii=False))


if __name__ == '__main__':
    main()
