"""Run: python clean_scores.py --input raw_scores.csv --output results
Requires pandas and matplotlib. Original input is never overwritten.
"""
from pathlib import Path
import argparse
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

def clean(raw):
    frame = raw.copy()
    if not {'student_id','name','team','score','date'} <= set(frame.columns):
        raise ValueError('missing required columns')
    frame['student_id'] = frame['student_id'].astype('string').str.strip()
    frame['name'] = frame['name'].astype('string').str.strip()
    frame['team'] = frame['team'].astype('string').str.strip().str.upper()
    duplicates = int(frame.duplicated().sum())
    frame = frame.drop_duplicates().copy()
    # Only exact duplicates are removed automatically; conflicting student/date rows are rejected.
    if frame.duplicated(['student_id','date']).any():
        raise ValueError('conflicting records for the same student and date')
    numeric = pd.to_numeric(frame['score'], errors='coerce')
    valid = numeric.between(0,100)
    frame['score'] = numeric.where(valid)
    frame['date'] = pd.to_datetime(frame['date'],format='%Y-%m-%d',errors='coerce')
    if frame['date'].isna().any() or not frame['team'].isin(['A','B']).all():
        raise ValueError('invalid date or team')
    if frame['student_id'].isna().any() or frame['student_id'].eq('').any():
        raise ValueError('missing student ID')
    audit = {'raw_rows':len(raw),'exact_duplicates_removed':duplicates,
             'rows_after_dedup':len(frame),'invalid_or_missing_scores':int(frame['score'].isna().sum())}
    return frame, audit

def main():
    parser=argparse.ArgumentParser()
    parser.add_argument('--input',type=Path,default=Path('raw_scores.csv'))
    parser.add_argument('--output',type=Path,default=Path('results'))
    args=parser.parse_args()
    raw=pd.read_csv(args.input,dtype={'student_id':'string'})
    frame,audit=clean(raw)
    summary=frame.groupby('team')['score'].agg(['count','mean']).reset_index()
    assert frame['student_id'].str.len().eq(3).all()
    assert len(frame)==7 and audit['invalid_or_missing_scores']==3
    assert summary['count'].tolist()==[2,2]
    assert summary['mean'].tolist()==[85.0,35.0]
    args.output.mkdir(parents=True,exist_ok=True)
    frame.to_csv(args.output/'clean_scores.csv',index=False)
    summary.to_csv(args.output/'summary.csv',index=False)
    fig,axes=plt.subplots(1,2,figsize=(9,4))
    axes[0].bar(summary['team'],summary['mean'],color=['#1688b8','#7c6ed4'])
    axes[0].set_ylim(0,100);axes[0].set_ylabel('Mean score (0-100)');axes[0].set_title('Valid scores only; n=2 per team')
    for i,row in summary.iterrows():axes[0].text(i,row['mean']+2,f"{row['mean']:.0f}",ha='center')
    axes[1].scatter(frame['team'],frame['score'],color='#1688b8',s=70)
    axes[1].set_ylim(-5,105);axes[1].set_ylabel('Individual score');axes[1].set_title('Individual scores reveal spread')
    fig.tight_layout();fig.savefig(args.output/'score-report.png',dpi=160);plt.close(fig)
    print(audit);print(summary.to_string(index=False))

if __name__=='__main__':main()
