""" Test National Capabilities snippet generation. """

import os
import shutil
import io

import lxml.html

from vasl_templates.webapp.tests.utils import init_webapp, get_nationalities, SwitchFrame

# ---------------------------------------------------------------------

def test_national_capabilities_reports( webapp, webdriver ):
    """Check the national capabilities reports."""

    # initialize
    webapp.control_tests.set_data_dir( "{REAL}" )
    init_webapp( webapp, webdriver )

    # initialize
    check_dir = os.path.join( os.path.split(__file__)[0], "fixtures/nat-caps/" )
    save_dir = os.environ.get( "NATCAPS_SAVEDIR" ) # nb: define this to save the generated reports
    if save_dir and os.path.isdir(save_dir):
        shutil.rmtree( save_dir )

    def do_test( nats, theater, years ): #pylint: disable=missing-docstring

        # initialize
        failed = False

        # check each nationality
        for nat in nats:

            # check each year
            for year in range( years[0], years[1]+1 ):

                # get the next snippet
                nat_caps = _get_nat_caps( webapp, webdriver, nat, theater, year, 1 )
                if nat in ("filipino",):
                    assert nat_caps is None
                    continue
                report = _make_report( nat, theater, year, nat_caps )

                # check if we should save the report
                fname = os.path.join(
                    os.path.join("kfw",nat) if theater == "Korea" else nat,
                    "{}.txt".format( year )
                )
                if save_dir:
                    fname2 = os.path.join( save_dir, fname )
                    os.makedirs( os.path.split(fname2)[0], exist_ok=True )
                    with open( os.path.join(save_dir,fname2), "w", encoding="utf-8" ) as fp:
                        fp.write( report )

                # check the report
                fname = os.path.join( check_dir, fname )
                with open( fname, "r", encoding="utf-8" ) as fp:
                    if fp.read() != report:
                        if save_dir:
                            print( "FAILED:", fname )
                            failed = True
                        else:
                            assert False, "Report mismatch: {}".format( fname )

        assert not failed

    # check each nationality
    nationalities = list( get_nationalities( webapp ).keys() )
    do_test(
        [ nat for nat in nationalities if not nat.startswith("kfw-") ],
        "ETO", (1940,1945)
    )
    do_test(
        [ "american", "kfw-rok", "british", "kfw-ounc", "kfw-kpa", "kfw-cpva" ],
        "Korea", (1950,1953)
    )

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def _get_nat_caps( webapp, webdriver, nat, theater, year, month ): #pylint: disable=too-many-locals
    """Get a national capabilities snippet."""

    # get the snippet
    url = webapp.url_for( "get_national_capabilities", nat=nat, theater=theater, year=year, month=month )
    webdriver.get( url )
    with SwitchFrame( webdriver, "#results" ):
        buf = webdriver.page_source

    # check if there is anything
    if "Not available." in buf:
        return None

    def to_text( elem ):
        """Convert an HTML element to text (tags are stripped, we don't descend into child nodes)."""
        vals = [ elem.text ]
        for c in elem.iterchildren():
            if c.tag == "ul":
                continue
            vals.extend( [ c.text, c.tail ] )
        vals.append( elem.tail )
        vals = [ v for v in vals if v ]
        return "".join( vals )

    # parse the basic details
    report = {}
    doc = lxml.html.fromstring( buf )
    fields = [ "grenades", "hob-drm", "th-color", "oba-black", "oba-red", "oba-access" ]
    for field in fields:
        elems = doc.xpath(  "//*[@class='{}']".format( field ) )
        if len(elems) == 0:
            report[ field ] = "-"
        else:
            assert len(elems) == 1
            report[ field ] = to_text( elems[0] ).strip()
    if report["hob-drm"] != "-":
        assert report["hob-drm"].startswith( "Heat of Battle: " )
        report["hob-drm"] = report["hob-drm"][16:]

    # parse the OBA comments
    report["oba-comments"] = []
    for elem in doc.xpath( "//ul[@class='oba-comments']/li" ):
        report["oba-comments"].append( elem.text.strip() )

    def parse_list( root, items, depth ):
        """Parse a list of items (and their child items)."""
        for elem in root.xpath( "./li" ):
            val = to_text( elem )
            val = val.strip()
            elems = elem.xpath( "./ul" )
            if not elems:
                items.append( val )
            else:
                assert len(elems) == 1
                children = []
                parse_list( elems[0], children, depth+1 )
                items.append( [ val, children ] )

    # parse the notes
    report["notes"] = []
    elems = doc.xpath( "//ul[@class='notes']" )
    if len(elems) == 0:
        pass
    else:
        assert len(elems) == 1
        parse_list( elems[0], report["notes"], 0 )

    return report

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def _make_report( nat, theater, year, nat_caps ):
    """Generate a report for the national capabilities."""

    def dump_list_items( items, depth ):
        """Dump a list of items (and their child items)."""
        tab = "  " * depth
        for item in items:
            if isinstance( item, str ):
                print( "{}* {}".format( tab, item ), file=buf )
            else:
                assert isinstance( item, list ) and len(item) == 2
                print( "{}* {}".format( tab, item[0] ), file=buf )
                dump_list_items( item[1], depth+1 )

    # generate the report
    buf = io.StringIO()
    print( "=== {} ({} {}) ===".format( nat, theater, year ), file=buf )
    print( "", file=buf )
    print( nat_caps["grenades"], file=buf )
    print( "HoB: {}".format( nat_caps["hob-drm"] ), file=buf )
    print( nat_caps["th-color"], file=buf )
    print( "OBA: {} {}".format( nat_caps["oba-black"], nat_caps["oba-red"] ), end="", file=buf )
    if nat_caps["oba-access"]:
        print( " {}".format( nat_caps["oba-access"] ), end="", file=buf )
    print( "", file=buf )
    for cmt in nat_caps["oba-comments"]:
        print( "- {}".format( cmt ), file=buf )
    if nat_caps["notes"]:
        print( "", file=buf )
        dump_list_items( nat_caps["notes"], 0 )

    return buf.getvalue()

# ---------------------------------------------------------------------

def test_time_based_national_capabilities( webapp, webdriver ):
    """Check time-based national capabilities.

    Capabilities that change according to the year are checked in the reports,
    these tests check those capabilities that change in the middle of a year.
    """

    # initialize
    webapp.control_tests.set_data_dir( "{REAL}" )
    init_webapp( webapp, webdriver )

    def check_notes( nat, theater, month, year, expected ):
        """Check the national capabilities notes."""
        nat_caps = _get_nat_caps( webapp, webdriver, nat, theater, year, month )
        for e in expected:
            notes = [
                n if isinstance(n,str) else n[0]
                for n in nat_caps["notes"]
            ]
            if e.startswith( "!" ):
                assert e[1:] not in notes
            else:
                assert e in notes

    def check_oba( nat, theater, month, year, expected_black, expected_red, comments=None, plentiful=None ):
        """Check the OBA national capabilities."""
        nat_caps = _get_nat_caps( webapp, webdriver, nat, theater, year, month )
        assert nat_caps["oba-black"] == expected_black
        assert nat_caps["oba-red"] == expected_red
        if plentiful:
            assert comments is None
            comments = [ "Plentiful Ammo included" ]
        if comments:
            assert nat_caps["oba-comments"] == comments
        else:
            assert not nat_caps["oba-comments"]

    def check_th_color( nat, theater, month, year, expected ):
        """Check the TH# color."""
        nat_caps = _get_nat_caps( webapp, webdriver, nat, theater, year, month )
        assert nat_caps["th-color"] == expected

    # test the German national capabilities
    check_notes( "german", "ETO", 12, 1942, [
        "No Inherent PF", "No Inherent ATMM"
    ] )
    check_notes( "german", "ETO", 9, 1943, [
        "No Inherent PF", "No Inherent ATMM"
    ] )
    check_notes( "german", "ETO", 10, 1943, [
        "Inherent PF", "No Inherent ATMM"
    ] )

    # test the Russian national capabilities
    check_notes( "russian", "ETO", 12, 1941, [
        "Commissars", "Riders NA"
    ] )
    check_notes( "russian", "ETO", 10, 1942, [
        "Commissars", "Riders OK"
    ] )
    check_notes( "russian", "ETO", 11, 1942, [
        "Commissars NA", "Riders OK"
    ] )
    check_notes( "russian", "ETO", 1, 1943, [
        "Commissars NA", "Riders OK"
    ] )

    # test the Finnish national capabilities
    # NOTE: We should test for Inherent PF here, but it's in a nested sub-list (more trouble than it's worth).
    check_oba( "finnish", "ETO", 12, 1943, "8B", "3R", plentiful=True )
    check_oba( "finnish", "ETO", 1, 1944, "8B", "3R", plentiful=True )
    check_oba( "finnish", "ETO", 9, 1944, "8B", "3R", plentiful=True )
    check_oba( "finnish", "ETO", 10, 1944, "7B", "3R", plentiful=True )
    check_oba( "finnish", "ETO", 1, 1945, "7B", "3R", plentiful=True )

    # test the Axis Minor national capabilities
    check_notes( "romanian", "ETO", 12, 1942, [
        "No Inherent ATMM"
    ] )
    check_notes( "romanian", "ETO", 6, 1943, [
        "No Inherent ATMM"
    ] )
    check_notes( "romanian", "ETO", 7, 1943, [
        "Inherent ATMM in Romanian non-Crew Elite & 1st Line MMC (-2 CC DRM)"
    ] )
    check_notes( "romanian", "ETO", 1, 1944, [
        "Inherent ATMM in Romanian non-Crew Elite & 1st Line MMC (-2 CC DRM)"
    ] )

    # test the KFW American national Capabilities
    # NOTE: We should test for early war Katusa here, but it's in a nested sub-list (more trouble than it's worth).
    check_oba( "american", "Korea", 12, 1949, "???", "3R" )
    check_oba( "american", "Korea", 5, 1950, "???", "3R" )
    check_oba( "american", "Korea", 6, 1950, "9B", "3R" )
    check_oba( "american", "Korea", 8, 1950, "9B", "3R" )
    check_oba( "american", "Korea", 9, 1950, "10B", "3R", plentiful=True )
    check_oba( "american", "Korea", 1, 1951, "10B", "3R", plentiful=True )
    check_th_color( "american", "Korea", 12, 1949, "??? TH#" )
    check_th_color( "american", "Korea", 5, 1950, "??? TH#" )
    check_th_color( "american", "Korea", 6, 1950, "Red TH#" )
    check_th_color( "american", "Korea", 8, 1950, "Red TH#" )
    check_th_color( "american", "Korea", 9, 1950, "Black TH#" )
    check_th_color( "american", "Korea", 1, 1951, "Black TH#" )
    check_notes( "american", "Korea", 5, 1950, [
        "!Early KW U.S. Army rules:"
    ] )
    check_notes( "american", "Korea", 6, 1950, [
        "Early KW U.S. Army rules:"
    ] )
    check_notes( "american", "Korea", 9, 1950, [
        "!Early KW U.S. Army rules:"
    ] )

    # test the South Korean national Capabilities
    check_oba( "kfw-rok", "Korea", 5, 1950, "???", "3R",
        comments = [ "Plentiful Ammo included (KMC)" ]
    )
    check_oba( "kfw-rok", "Korea", 6, 1950, "10B", "3R",
        comments = [ "Plentiful Ammo included (KMC)", "ROK: 6B/3R" ]
    )
    check_oba( "kfw-rok", "Korea", 10, 1950, "10B", "3R", plentiful=True )
    check_th_color( "kfw-rok", "Korea", 8, 1950, "Red TH#" )
    check_th_color( "kfw-rok", "Korea", 9, 1950, "Red TH# (ROK) ; Black (KMC)" )
    check_th_color( "kfw-rok", "Korea", 5, 1951, "Black TH#" )

    # test the CPVA national Capabilities
    check_notes( "kfw-cpva", "Korea", 9, 1950, [
        "!Early KW CPVA rules"
    ] )
    check_notes( "kfw-cpva", "Korea", 10, 1950, [
        "Early KW CPVA rules"
    ] )
    check_notes( "kfw-cpva", "Korea", 4, 1951, [
        "!Early KW CPVA rules"
    ] )
    check_oba( "kfw-cpva", "Korea", 3, 1951, "-", "-" )
    check_oba( "kfw-cpva", "Korea", 4, 1951, "7B", "3R" )
    check_oba( "kfw-cpva", "Korea", 11, 1952, "7B", "2R" )
