"""Read an authorised GitHub repository through gh; never modify it."""
import argparse
import json
import os
import re
import subprocess
from datetime import datetime, timezone
from pathlib import Path


def api(path):
    result = subprocess.run(['gh', 'api', path], capture_output=True, text=True, timeout=45)
    if result.returncode:
        raise RuntimeError('GitHub request failed. Check gh authentication and repository access.')
    return json.loads(result.stdout)


def collect(repo, incident_at, service, environment, include_deployments=False):
    if not re.fullmatch(r'[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+', repo):
        raise ValueError('Use owner/repository format.')
    parsed = datetime.fromisoformat(incident_at.replace('Z', '+00:00'))
    if parsed.tzinfo is None:
        raise ValueError('Incident time needs a timezone.')
    # Limited collection is recorded explicitly; a commit is not a deployment.
    commits = api(f'repos/{repo}/commits?per_page=100')
    runs = api(f'repos/{repo}/actions/runs?per_page=100')
    events = []
    for commit in commits:
        events.append({'id': 'commit:' + commit['sha'], 'at': commit['commit']['committer']['date'],
                       'kind': 'change', 'service': service, 'environment': 'repository',
                       'summary': 'Commit: ' + (commit['commit']['message'].splitlines() or ['(no commit message)'])[0][:500], 'url': commit['html_url']})
    for run in runs.get('workflow_runs', []):
        events.append({'id': 'workflow:' + str(run['id']) + ':' + str(run.get('run_attempt', 1)),
                       'at': run.get('run_started_at') or run['created_at'], 'kind': 'symptom',
                       'service': service, 'environment': 'ci',
                       'summary': (run.get('name') or 'Workflow') + ': ' + str(run.get('conclusion') or run.get('status')),
                       'url': run['html_url']})
    deployment_coverage = ' Deployment status collection was not requested.'
    if include_deployments:
        deployments = api(f'repos/{repo}/deployments?per_page=20')
        if not isinstance(deployments, list):
            raise ValueError('Invalid GitHub deployments response.')
        for deployment in deployments[:20]:
            deployment_id = deployment['id']
            if not isinstance(deployment_id, int) or isinstance(deployment_id, bool) or deployment_id < 1:
                raise ValueError('Invalid GitHub deployment identifier.')
            statuses = api(f'repos/{repo}/deployments/{deployment_id}/statuses?per_page=20')
            if not isinstance(statuses, list):
                raise ValueError('Invalid GitHub deployment status response.')
            for status in statuses[:20]:
                state = status['state']
                # A request or pending status does not establish that code went live.
                # Retain unsuccessful transitions as context, never causal changes.
                events.append({
                    'id': f'deployment:{deployment_id}:status:{status["id"]}',
                    'at': status['created_at'],
                    'kind': 'change' if state == 'success' else 'deployment-status',
                    'service': service,
                    'environment': status.get('environment') or deployment['environment'],
                    'summary': f'GitHub deployment {deployment_id}: {state}; revision {deployment["sha"]}',
                    'url': f'https://github.com/{repo}/deployments',
                })
        deployment_coverage = (' Latest 20 deployment requests and up to 20 statuses per deployment; '
            'only success statuses become change candidates. Statuses are reported by the deployment integration, '
            'not independent verification of production health. Older deployments/statuses may be omitted.')
    return {'incident': {'at': parsed.isoformat(), 'service': service, 'environment': environment},
            'events': events, 'dependencies': [], 'collection': {'repository': repo, 'collectedAt': datetime.now(timezone.utc).isoformat(),
            'coverage': 'Latest 100 commits and 100 workflow runs. Commit events use repository environment; they do not prove production deployment. Workflow events use ci environment. Add verified deployment/configuration evidence for production investigations.' + deployment_coverage}}


def main():
    p = argparse.ArgumentParser(description=__doc__)
    for arg in ['repo', 'incident-at', 'service', 'environment', 'output']:
        p.add_argument('--' + arg, required=True)
    p.add_argument('--include-deployments', action='store_true', help='Read deployment statuses using existing repository read access.')
    a = p.parse_args()
    result = collect(a.repo, a.incident_at, a.service, a.environment, a.include_deployments)
    with open(a.output, 'x', encoding='utf-8', opener=lambda path, flags: os.open(path, flags, 0o600)) as handle:
        json.dump(result, handle, indent=2)
    print(f'Saved {len(result["events"])} real GitHub events. Collection coverage is recorded in the output.')


if __name__ == '__main__':
    try:
        main()
    except (OSError, ValueError, RuntimeError, subprocess.TimeoutExpired) as error:
        raise SystemExit(str(error))
