"""Resume operational preprocessing or postprocess existing raw output without cleanup."""
from pathlib import Path
import datetime as dt
import re
import rcms as core


def discover(cfg, cycle, model):
    base = Path(cfg['legacy_dirs'][model])
    for branch in ('wdir2', 'wdir'):
        work = base / branch / cycle
        for raw in (work/'output', work) if model == 'wrf' else (work/'output',):
            pattern = 'wrfout_d01_*' if model == 'wrf' else 'd01_ATM.*.nc'
            if any(raw.glob(pattern)):
                return {'action':'ADOPT_POST', 'raw':str(raw),
                        'reason':'Existing model output: validate and postprocess; no integration rerun'}
    work = base/'wdir'/cycle
    output = work/'output'
    if output.exists() and any(output.iterdir()):
        return {'action':'BLOCKED', 'reason':'Nonempty output without recognized model data: '+str(output)}
    if model == 'wrf' and any(work.glob('met_em.d01.*.nc')):
        return {'action':'RESUME', 'work':str(work), 'reason':'Validate existing met_em, then REAL/WRF and postprocessing'}
    if model == 'regcm' and (work/'input/d01_SST.nc').is_file() and any((work/'input').glob('d01_ICBC.*.nc')):
        return {'action':'RESUME', 'work':str(work), 'reason':'Validate existing SST/ICBC, then RegCM and postprocessing'}
    return None


def output_dir(man, model, work):
    source = man.get('recovery', {}).get(model)
    if source and source['action'] == 'RESUME':
        dest = Path(source['work'])/'output'
        core.need(not dest.exists() or (dest.is_dir() and not any(dest.iterdir())),
                  'Refusing nonempty integration output: '+str(dest))
        dest.mkdir(exist_ok=True)
        return dest
    dest = work/('output' if man['config'].get('operational_layout') else 'raw')
    dest.mkdir()
    return dest


def timestamps(r, path):
    r.nc(path)
    return re.findall(r'\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d', r.run(['cdo','-s','showtimestamp',path]))


def check_regcm_input(man, source, r):
    start,end=man['cycle'],man['end']
    inp=source/'input'
    r.axis(inp/'d01_SST.nc',start,end,6)
    actual=set()
    for p in sorted(inp.glob('d01_ICBC.*.nc')):
        actual.update(timestamps(r,p))
    expected={core.iso(t).replace('_','T') for t in core.times(start,end)}
    core.need(expected.issubset(actual), 'Existing ICBC has missing timestamps; integration refused')
    for name in ('d01_DOMAIN000.nc','d01_CLM45_surface.nc'):
        r.nc(inp/name)
    return inp


def resume(man, model, work, r):
    source=Path(man['recovery'][model]['work'])
    # New namelists, logs and REAL files belong to this attempt, never overwrite old ones.
    if model == 'wrf':
        return core.wrf_from_met(man,work,r,source)
    inp=check_regcm_input(man,source,r)
    raw=output_dir(man,model,work)
    nml=core.render(core.template('regcm.nml'),man['cycle'],man['end'])
    for key,value in [('dirter',inp),('dirglob',inp),('inpter',Path(man['config']['regcm_dataset'])),
                      ('inpglob',Path(man['config']['regcm_dataset'])),('dirout',raw)]:
        nml=core.set_nml(nml,key,"'%s'" % value)
    core.put(work/'namelist.in',nml)
    core.put(work/'PREPROCESS_SUCCESS.json',{'end':man['end'],'reused':str(source)})
    return core.regcm_from_input(man,work,r,raw)


def validate_raw(man, model, raw, r):
    """Use available raw coverage; require complete, continuous streams before post."""
    start=man['cycle']
    if model == 'wrf':
        files=sorted(raw.glob('wrfout_d01_*'))
        core.need(files,'No WRF output')
        dates=[dt.datetime.strptime(p.name[len('wrfout_d01_'):],'%Y-%m-%d_%H:%M:%S') for p in files]
        end=dates[-1].strftime('%Y%m%d%H')
        expected=list(core.times(start,end))
        core.need([d.strftime('%Y%m%d%H') for d in dates]==expected,'Incomplete WRF output sequence')
        for p,d in zip(files,dates):
            r.nc(p)
            core.need(d.strftime('%Y-%m-%d_%H:%M:%S') in r.run(['ncdump','-v','Times',p]),'WRF timestamp mismatch: '+str(p))
        core.need(any(raw.glob('wrfxtrm_d01_*')),'WRF extremes missing')
    else:
        streams={}
        for family,step in [('ATM',6),('SRF',3),('RAD',6),('STS',24)]:
            axis=[]
            for p in sorted(raw.glob('d01_'+family+'.*.nc')): axis+=timestamps(r,p)
            core.need(axis,'Missing RegCM '+family)
            parsed=[dt.datetime.strptime(t,'%Y-%m-%dT%H:%M:%S') for t in axis]
            core.need(all(b-a==dt.timedelta(hours=step) for a,b in zip(parsed,parsed[1:])), 'Noncontinuous RegCM '+family)
            core.need(parsed[0]<=core.date10(start)+dt.timedelta(hours=step),'RegCM '+family+' starts too late')
            streams[family]=parsed
        end=streams['ATM'][-1].strftime('%Y%m%d%H')
        for family in ('SRF','RAD'):
            core.need(streams[family][-1]==core.date10(end),'RegCM streams end at different times')
        core.need(streams['STS'][-1]>=core.date10(end)-dt.timedelta(hours=24),'RegCM daily output truncated')
    core.need(core.date10(end)>core.date10(start),'Raw output interval is empty')
    return dict(man,end=end)


def check_met(man, met, r):
    nml=core.template('wrf.nml')
    nx=int(re.search(r'(?m)^\s*e_we\s*=\s*(\d+)',nml)[1])
    ny=int(re.search(r'(?m)^\s*e_sn\s*=\s*(\d+)',nml)[1])
    levels=None
    for t in core.times(man['cycle'],man['end']):
        p=met/('met_em.d01.'+core.iso(t)+'.nc')
        header=r.nc(p)
        for key,value in [('west_east',nx-1),('south_north',ny-1),('num_st_layers',4)]:
            match=re.search(r'(?m)^\s*'+key+r'\s*=\s*(\d+)\s*;',header)
            core.need(match and int(match[1])==value,'Metgrid dimension differs from namelist: '+key+' '+str(p))
        match=re.search(r'num_metgrid_levels\s*=\s*(\d+)',header)
        core.need(match,'Missing metgrid levels')
        core.need(levels is None or levels==match[1],'Metgrid levels changed across input times')
        levels=match[1]
        core.need(core.iso(t) in r.run(['ncdump','-v','Times',p]),'Metgrid timestamp mismatch: '+str(p))
    return levels
