#!/usr/bin/env python3
# vim: wrapmargin=0 textwidth=0 smarttab expandtab tabstop=2 shiftwidth=2
"""Produce a report on the provided EDDN Gateway log file's ERRORs."""

import argparse
import fileinput
import re

import semantic_version


def parse_cl_args() -> str:
  """
  Check command-line arguments for input file name.
  
  :returns: str - input file name
  """
  parser = argparse.ArgumentParser(
    prog='eddn-report-log-errors',
    description='Process an EDDN Gateway log file and report on any ERROR lines found'
  )

  parser.add_argument(
    'inputfile',
    metavar='<input file name>',
    help='Name of an EDDN Gateway log file'
  )

  args = parser.parse_args()

  return args.inputfile


def process_file(input_file: str) -> None:
  print(f'Input file: {input_file}')
  
  _RE_ERROR = re.compile(
    r'^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}[\.,][0-9]{3} - ERROR - Gateway:[0-9]+:'
    r' (?P<err_msg>.+)'
    r' \((?P<request_size>[0-9]+),'
    r' "(?P<uploader_id>[^"]*)",'
    r' "(?P<software_name>[^"]*)",'
    r' "(?P<software_version>[^"]*)",'
    r' "(?P<schema_ref>[^"]*)",'
    r' "(?P<journal_event>[^"]*)"\)'
    r' from (?P<sender_ip>.+)$'
  )
  # TODO: Make this handle gzipped files
  with fileinput.FileInput(files=(input_file), mode='r') as input:
    line = input.readline()
    while line:
      line = line.strip()
      matches = _RE_ERROR.search(line)
      if matches:
        # print(matches.group('err_msg'))
        # print(matches.group('request_size'))
        # print(matches.group('uploader_id'))
        # print(matches.group('software_name'))
        # print(matches.group('software_version'))
        # print(matches.group('schema_ref'))
        # print(matches.group('journal_event'))
        # print(matches.group('sender_ip'))
        # print('')

        software_version = semantic_version.Version.coerce(matches.group('software_version'))
        ###################################################################
        # Issues we know about and HAVE already alerted their
        # developers to.
        ###################################################################
        if matches.group('software_name') == 'EDDiscovery':
          # https://github.com/EDDiscovery/EDDiscovery/releases/latest
          if software_version >= semantic_version.Version.coerce('15.0.0.0'):
            if matches.group('schema_ref') == 'https://eddn.edcd.io/schemas/approachsettlement/1':
              if matches.group('err_msg') == 'Failed Validation "[<ValidationError: "\'Latitude\' is a required property">]"':
                # <https://github.com/EDDiscovery/EDDiscovery/issues/3236>
                pass
              
              elif matches.group('err_msg') == 'Failed Validation "[<ValidationError: "Additional properties are not allowed (\'Name_Localised\' was unexpected)">]"':
                # <https://github.com/EDDiscovery/EDDiscovery/issues/3237>
                pass

              else:
                print(line)

            else:
              print(line)

          elif software_version >= semantic_version.Version.coerce('12.1.7.0'):
            if matches.group('schema_ref') in (
              'https://eddn.edcd.io/schemas/shipyard/2',
              'https://eddn.edcd.io/schemas/outfitting/2',
            ):
              # Reported via Discord PM to Robby 2022-01-07
              if matches.group('err_msg') == 'Failed Validation "[<ValidationError: \'[] is too short\'>]"':
                pass

              else:
                print(line)

            else:
              print(line)

        elif matches.group('software_name') == 'EDDLite':
          # https://github.com/EDDiscovery/EDDLite/releases/tag/latest
          if software_version >= semantic_version.Version.coerce('2.0.0'):
            if matches.group('schema_ref') in (
              'https://eddn.edcd.io/schemas/shipyard/2',
              'https://eddn.edcd.io/schemas/outfitting/2',
            ):
              # Failed Validation "[<ValidationError: "'2022-02-17T14.25.19Z' is not a 'date-time'">]"
              if (
                matches.group('err_msg').startswith('Failed Validation "[<ValidationError: "') and
                matches.group('err_msg').endswith('\' is not a \'date-time\'">]"')
              ):
                # <https://github.com/EDDiscovery/EDDLite/issues/8>
                pass

              elif matches.group('err_msg') == 'Failed Validation "[<ValidationError: \'[] is too short\'>]"':
                # Reported via Discord PM to Robby 2022-01-07
                pass

              else:
                print(line)

            else:
              print(line)

        elif matches.group('software_name') == 'EDDI':
          # https://github.com/EDCD/EDDI/releases/latest
          if software_version >= semantic_version.Version.coerce('4.0.1'):
            print(line)

        elif matches.group('software_name').startswith('E:D Market Connector'):
          # https://github.com/EDCD/EDMarketConnector/releases/latest
          if software_version >= semantic_version.Version.coerce('5.3.0'):
            if matches.group('schema_ref') == 'https://eddn.edcd.io/schemas/codexentry/1':
              # <https://github.com/EDCD/EDMarketConnector/issues/1393>
              if matches.group('err_msg') == 'Failed Validation "[<ValidationError: "\'\' is too short">]"':
                pass

              else:
                print(matches.group('err_msg'))
                print(line)

            elif matches.group('schema_ref') == 'https://eddn.edcd.io/schemas/journal/1':
              # <https://github.com/EDCD/EDMarketConnector/issues/1403>
              if matches.group('err_msg') == 'Failed Validation "[<ValidationError: "\'SystemAddress\' is a required property">]"':
                # <https://github.com/EDCD/EDMarketConnector/issues/1403>
                pass

              elif matches.group('err_msg').startswith(
                'Failed Validation "[<ValidationError: "{\'type\': [\'array\', \'boolean\', \'integer\', \'number\', \'null\', \'object\', \'string\']} is not allowed for'
                ):
                # <https://github.com/EDCD/EDMarketConnector/issues/1403>
                pass

              else:
                print(matches.group('err_msg'))
                print(line)

            elif matches.group('schema_ref') == 'https://eddn.edcd.io/schemas/fssdiscoveryscan/1':
              if matches.group('err_msg') == 'Failed Validation "[<ValidationError: "None is not of type \'boolean\'">]"':
                # <https://github.com/EDCD/EDMarketConnector/issues/1403>
                pass

              else:
                print(line)

            elif matches.group('schema_ref') == 'https://eddn.edcd.io/schemas/approachsettlement/1':
              if matches.group('err_msg') == 'Failed Validation "[<ValidationError: "\'Latitude\' is a required property">]"':
                # <https://github.com/EDCD/EDMarketConnector/issues/1476>
                pass
            
              else:
                print(line)

            else:
              print(line)

        elif matches.group('software_name') == 'Elite G19s Companion App':
          # <https://edcodex.info/?m=tools&entry=212>
          if software_version >= semantic_version.Version.coerce('3.7.7888.21039'):
            if matches.group('schema_ref') == 'https://eddn.edcd.io/schemas/commodity/3':
              if matches.group('err_msg') == 'Failed Validation "[<ValidationError: "Additional properties are not allowed (\'Proportion\', \'Name\' were unexpected)">]"':
              # Reported via Frontier forums: <https://forums.frontier.co.uk/threads/elite-g19s-companion-app-with-simulated-space-traffic-control.226782/post-9690204>
                pass

              else:
                print(line)
            
            else:
              print(line)

        elif matches.group('software_name') == 'EDSM':
          # It's in-browser, no public source/releases
          if software_version >= semantic_version.Version.coerce('1.0.3'):
            if matches.group('schema_ref') == 'https://eddn.edcd.io/schemas/journal/1':
              if matches.group('journal_event') == 'Scan':
                # <https://github.com/EDSM-NET/FrontEnd/issues/472>
                if matches.group('err_msg').startswith(
                    'Failed Validation "[<ValidationError: "None is not of type \'integer\'">]"'
                  ):
                  pass

                elif (
                    matches.group('err_msg').startswith('Failed Validation "[<ValidationError: "{') and 
                    matches.group('err_msg').endswith('} is not of type \'array\'">]"')
                ):
                  # <https://github.com/EDSM-NET/FrontEnd/issues/473>
                  pass

                else:
                  print(matches.group('err_msg'))
                  print(line)

              else:
                print(line)

            else:
              print(line)

        elif matches.group('software_name') == 'EDSM - Console':
          # It's in-browser, no public source/releases
          if software_version >= semantic_version.Version.coerce('1.0'):
            if matches.group('schema_ref') == 'https://eddn.edcd.io/schemas/journal/1':
              if matches.group('journal_event') == 'Scan':
                # <https://github.com/EDSM-NET/FrontEnd/issues/466>
                if not matches.group('err_msg').startswith(
                    'Failed Validation "[<ValidationError: "{\'type\': [\'array\', \'boolean\', \'integer\', \'number\', \'null\', \'object\', \'string\']} is not allowed for '
                  ):
                  print(matches.group('err_msg'))
                  print(line)

              else:
                print(line)

            else:
              print(line)

        elif matches.group('software_name') == 'EDAOS':
          # Apparently a Barry Carylon project, but no home page ?
          if software_version >= semantic_version.Version.coerce('1.2.3'):
            if matches.group('schema_ref') == 'https://eddn.edcd.io/schemas/journal/1':
              if matches.group('journal_event') == 'Docked':
                # <https://discord.com/channels/164411426939600896/205369618284544000/929102478954340372>
                if matches.group('err_msg').startswith(
                    'Failed Validation "[<ValidationError: "{\'type\': [\'array\', \'boolean\', \'integer\', \'number\', \'null\', \'object\', \'string\']} is not allowed for '
                  ):
                  pass

                print(matches.group('err_msg'))
                print(line)

              else:
                print(line)

            else:
              print(line)

        elif matches.group('software_name') == 'EliteLogAgent':
          # <https://github.com/DarkWanderer/Elite-Log-Agent>
          if software_version >= semantic_version.Version.coerce('2.0.0.660'):
            print(line)

        # <https://edcodex.info/?m=tools&entry=440>
        # <https://bitbucket.org/JuustoKakku/moonlight/src/master/>
        elif matches.group('software_name') == 'Moonlight':
          if matches.group('software_version') == '1.3.4':
            if matches.group('schema_ref') == 'https://eddn.edcd.io/schemas/journal/1':
              if matches.group('journal_event') == 'Scan':
                # Ref: <https://bitbucket.org/JuustoKakku/moonlight/issues/5/bad-scan-events-being-sent-to-eddn>
                if not matches.group('err_msg').startswith(
                    'Failed Validation "[<ValidationError: "{\'type\': [\'array\', \'boolean\', \'integer\', \'number\', \'null\', \'object\', \'string\']} is not allowed for \''
                  ):
                  print(matches.group('err_msg'))
                  print(line)

              else:
                print(line)

            else:
              print(line)

        # Abandoned/unmaintained project
        # <https://forums.frontier.co.uk/threads/release-eva-elite-virtual-assistant-for-iphone-ipad-no-longer-working-jan-2020.245900/page-18>
        # <https://apps.apple.com/gb/app/eva/id1098763533>
        elif matches.group('software_name') in ('EVA [iPhone]', 'EVA [iPad]'):
          pass

        ###################################################################
        # Issues we know about, but haven't yet alerted developers to
        ###################################################################
        ###################################################################
        else:
          print(line)

      line = input.readline()
      


if __name__ == "__main__":
  input_file = parse_cl_args()

  process_file(input_file)