#!/usr/bin/env python3
"""
RTExit Finding Tracker
Manages findings across a Red Team engagement.
Usage:
  python3 finding_tracker.py add "SQL Injection" CRITICAL 9.8 "target.com/login"
  python3 finding_tracker.py list [--severity CRITICAL]
  python3 finding_tracker.py show F-001
  python3 finding_tracker.py export [--format html|md|csv]
  python3 finding_tracker.py stats
"""

import argparse
import csv
import datetime
import json
import os
import sys


FINDINGS_CSV = os.path.join(
    os.environ.get('RTEXIT_OUTPUT', '_rtexit-output'),
    'docs', 'findings', 'findings-master.csv'
)

SEVERITY_ORDER = {'CRITICAL': 0, 'HIGH': 1, 'MEDIUM': 2, 'LOW': 3, 'INFO': 4}
SEVERITY_ICONS = {'CRITICAL': '🔴', 'HIGH': '🟠', 'MEDIUM': '🟡', 'LOW': '🔵', 'INFO': '⚪'}

FIELDNAMES = [
    'id', 'title', 'severity', 'cvss', 'status', 'asset',
    'cwe', 'cve', 'mitre', 'phase', 'date', 'operator', 'notes'
]


def ensure_csv():
    os.makedirs(os.path.dirname(FINDINGS_CSV), exist_ok=True)
    if not os.path.exists(FINDINGS_CSV):
        with open(FINDINGS_CSV, 'w', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=FIELDNAMES)
            writer.writeheader()


def load_findings() -> list:
    ensure_csv()
    with open(FINDINGS_CSV, 'r', encoding='utf-8') as f:
        return list(csv.DictReader(f))


def save_findings(findings: list):
    ensure_csv()
    with open(FINDINGS_CSV, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=FIELDNAMES)
        writer.writeheader()
        writer.writerows(findings)


def next_id(findings: list) -> str:
    if not findings:
        return 'F-001'
    last = sorted([f['id'] for f in findings if f['id'].startswith('F-')])[-1]
    num = int(last.split('-')[1]) + 1
    return f'F-{num:03d}'


def cmd_add(args):
    findings = load_findings()
    fid = next_id(findings)
    finding = {
        'id': fid,
        'title': args.title,
        'severity': args.severity.upper(),
        'cvss': args.cvss,
        'status': args.status or 'CONFIRMED',
        'asset': args.asset or '',
        'cwe': args.cwe or '',
        'cve': args.cve or '',
        'mitre': args.mitre or '',
        'phase': args.phase or '',
        'date': datetime.date.today().isoformat(),
        'operator': args.operator or '',
        'notes': args.notes or '',
    }
    findings.append(finding)
    save_findings(findings)

    # Create individual finding MD file
    md_path = os.path.join(os.path.dirname(FINDINGS_CSV), f'{fid}.md')
    icon = SEVERITY_ICONS.get(finding['severity'], '⚪')
    with open(md_path, 'w', encoding='utf-8') as f:
        f.write(f"""---
id: {fid}
title: "{finding['title']}"
severity: {finding['severity']}
cvss: {finding['cvss']}
status: {finding['status']}
asset: {finding['asset']}
cwe: {finding['cwe']}
cve: {finding['cve']}
mitre: {finding['mitre']}
date: {finding['date']}
---

# {icon} {fid} — {finding['title']}

## Summary
> **Severity:** {finding['severity']} | **CVSS:** {finding['cvss']} | **Asset:** {finding['asset']}

## Description
<!-- Describe the vulnerability in detail -->

## Technical Evidence
```
<!-- Paste command output, HTTP requests/responses, screenshots references -->
```

## Impact
<!-- Business and technical impact -->

## Reproduction Steps
1.
2.
3.

## Remediation
### Immediate (0-24 hours)
-

### Short-term (1-30 days)
-

### Long-term
-

## References
- CWE: https://cwe.mitre.org/data/definitions/{finding['cwe'].replace('CWE-', '')}.html
- MITRE ATT&CK: https://attack.mitre.org/techniques/{finding['mitre']}/
""")

    print(f"✅ Added: {fid} — {finding['title']} [{finding['severity']}]")
    print(f"   File: {md_path}")


def cmd_list(args):
    findings = load_findings()
    if args.severity:
        findings = [f for f in findings if f['severity'] == args.severity.upper()]
    if args.status:
        findings = [f for f in findings if f['status'] == args.status.upper()]

    findings.sort(key=lambda f: SEVERITY_ORDER.get(f['severity'], 99))

    if not findings:
        print("No findings found.")
        return

    print(f"\n{'ID':<8} {'SEVERITY':<10} {'CVSS':<6} {'STATUS':<12} {'TITLE'}")
    print("-" * 80)
    for f in findings:
        icon = SEVERITY_ICONS.get(f['severity'], '⚪')
        print(f"{f['id']:<8} {icon} {f['severity']:<8} {f['cvss']:<6} {f['status']:<12} {f['title']}")
    print(f"\nTotal: {len(findings)} findings")


def cmd_stats(args):
    findings = load_findings()
    counts = {}
    for f in findings:
        counts[f['severity']] = counts.get(f['severity'], 0) + 1

    print("\n=== Finding Statistics ===")
    for sev in ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO']:
        icon = SEVERITY_ICONS.get(sev, '⚪')
        count = counts.get(sev, 0)
        bar = '█' * count
        print(f"{icon} {sev:<10}: {count:>3}  {bar}")
    print(f"\n   TOTAL    : {len(findings)}")


def cmd_export(args):
    findings = load_findings()
    fmt = args.format or 'md'

    if fmt == 'csv':
        print(open(FINDINGS_CSV).read())

    elif fmt == 'json':
        print(json.dumps(findings, indent=2))

    elif fmt == 'md':
        print("# Findings Summary\n")
        print(f"| ID | Severity | CVSS | Title | Asset | Status |")
        print(f"|----|---------:|-----:|-------|-------|--------|")
        for f in sorted(findings, key=lambda x: SEVERITY_ORDER.get(x['severity'], 99)):
            icon = SEVERITY_ICONS.get(f['severity'], '⚪')
            print(f"| {f['id']} | {icon} {f['severity']} | {f['cvss']} | {f['title']} | {f['asset']} | {f['status']} |")

    elif fmt == 'html':
        print("<table>")
        print("<tr><th>ID</th><th>Severity</th><th>CVSS</th><th>Title</th><th>Asset</th></tr>")
        for f in findings:
            print(f"<tr><td>{f['id']}</td><td>{f['severity']}</td><td>{f['cvss']}</td>"
                  f"<td>{f['title']}</td><td>{f['asset']}</td></tr>")
        print("</table>")


def main():
    parser = argparse.ArgumentParser(description='RTExit Finding Tracker')
    sub = parser.add_subparsers(dest='command')

    # add
    p_add = sub.add_parser('add', help='Add a new finding')
    p_add.add_argument('title', help='Finding title')
    p_add.add_argument('severity', help='CRITICAL/HIGH/MEDIUM/LOW/INFO')
    p_add.add_argument('cvss', help='CVSS score (e.g. 9.8)')
    p_add.add_argument('asset', nargs='?', help='Affected asset URL/IP')
    p_add.add_argument('--status', default='CONFIRMED')
    p_add.add_argument('--cwe', default='')
    p_add.add_argument('--cve', default='')
    p_add.add_argument('--mitre', default='')
    p_add.add_argument('--phase', default='')
    p_add.add_argument('--operator', default='')
    p_add.add_argument('--notes', default='')

    # list
    p_list = sub.add_parser('list', help='List findings')
    p_list.add_argument('--severity', help='Filter by severity')
    p_list.add_argument('--status', help='Filter by status')

    # stats
    sub.add_parser('stats', help='Show finding statistics')

    # export
    p_exp = sub.add_parser('export', help='Export findings')
    p_exp.add_argument('--format', choices=['md', 'csv', 'json', 'html'], default='md')

    args = parser.parse_args()

    if args.command == 'add':
        cmd_add(args)
    elif args.command == 'list':
        cmd_list(args)
    elif args.command == 'stats':
        cmd_stats(args)
    elif args.command == 'export':
        cmd_export(args)
    else:
        parser.print_help()


if __name__ == '__main__':
    main()
