#!/usr/bin/python3
# encoding=utf-8
#
# Copyright © 2015 Simon McVittie <smcv@debian.org>
# SPDX-License-Identifier: GPL-2.0-or-later

from __future__ import annotations

import difflib
import json
import os
import sys
import time
import yaml
from contextlib import suppress
from typing import (Any, TYPE_CHECKING)

if 'GDP_UNINSTALLED' not in os.environ:
    sys.path.insert(0, '/usr/share/game-data-packager')
    sys.path.insert(0, '/usr/share/games/game-data-packager')

from game_data_packager.game import (load_games, load_game)
from game_data_packager.util import ascii_safe

if TYPE_CHECKING:
    from game_data_packager.game import (GameData)


def dump(serialized: dict[str, Any]) -> str:
    return json.dumps(serialized, sort_keys=True, indent=2)


def yaml_dump(serialized: dict[str, Any]) -> str:
    dump = yaml.dump(serialized, default_flow_style=False)
    assert type(dump) is str
    return dump


def compare(
    to_data: dict[str, Any],
    to_json: str,
    label: str,
    expected_data: dict[str, Any],
    expected_json: str,
    expected_label: str
) -> bool:
    if to_json == expected_json:
        return True

    to_yaml = yaml_dump(to_data)
    expected_to_yaml = yaml_dump(expected_data)

    if to_yaml == expected_to_yaml:
        print('# JSON differs but YAML is the same?!')
        sys.stdout.writelines(difflib.unified_diff(
            to_json.splitlines(True),
            expected_json.splitlines(True),
            label, expected_label, n=50))
    else:
        sys.stdout.writelines(difflib.unified_diff(
            to_yaml.splitlines(True),
            expected_to_yaml.splitlines(True),
            label, expected_label, n=50))

    return False


if __name__ == '__main__':
    games = '*'

    output = os.environ.get('GDP_BUILDDIR', 'out')

    if 'DEB_BUILD_TIME_TESTS' in os.environ:
        print('# SKIP: not doing memory-hungry test at build-time')
        sys.exit(0)

    if len(sys.argv) > 1:
        assert len(sys.argv) == 2
        games = sys.argv[1]

    from_ref: dict[str, GameData] | None
    if os.path.exists('ref.zip'):
        t = time.process_time()
        # usage:
        # make
        # cp out/vfs.zip ref.zip
        # make
        # make check
        from_ref = load_games(games, use_vfs='ref.zip')
        dt = time.process_time() - t
        print('# loaded game data from ref.zip in %.3f seconds' % dt,
              flush=True)
    else:
        from_ref = None

    t = time.process_time()
    from_vfs = load_games(games)
    dt = time.process_time() - t
    print('# loaded game data from vfs.zip in %.3f seconds' % dt, flush=True)

    t = time.process_time()
    from_json = load_games(games, use_vfs=False)
    dt = time.process_time() - t
    print('# loaded game data from JSON in %.3f seconds' % dt, flush=True)

    t = time.process_time()
    from_yaml = load_games(games, datadir='data', use_vfs=False)
    dt = time.process_time() - t
    print('# loaded game data from YAML in %.3f seconds' % dt)

    assert set(from_vfs.keys()) == set(from_json.keys())
    assert set(from_vfs.keys()) == set(from_yaml.keys())

    if from_ref is not None:
        assert set(from_vfs.keys()) == set(from_ref.keys())

    fail = False

    for (name, game) in sorted(from_vfs.items()):
        print('# %s -----------------------------------------' % name)

        with suppress(FileNotFoundError):
            os.remove(os.path.join(output, '%s-unexpanded-yaml.txt' % name))

        # Do this one without checking consistency, so that we can assert
        # that skipping the consistency checks does not affect the outcome
        game.load_file_data(check=False)
        ascii_safe(game.longname, force=True).encode('ascii')
        ascii_safe(game.help_text, force=True).encode('ascii')
        vfs_to_data = game.to_data()
        vfs_to_json = dump(vfs_to_data)

        json_game = from_json[name]
        json_game.load_file_data(check=True, use_vfs=False)
        json_to_data = json_game.to_data()
        json_to_json = dump(json_to_data)
        json_to_data_unexpanded = json_game.to_data(
                expand=False, include_ignorable=True)
        json_to_json_unexpanded = dump(json_to_data_unexpanded)

        yaml_game = from_yaml[name]
        yaml_game.load_file_data(check=True, datadir='data')
        yaml_to_data = yaml_game.to_data()
        yaml_to_json = dump(yaml_to_data)

        if not compare(
                yaml_to_data, yaml_to_json, '%s loaded from YAML' % name,
                vfs_to_data, vfs_to_json, '%s loaded from vfs.zip' % name):
            fail = True

        if not compare(
                json_to_data, json_to_json, '%s loaded from JSON' % name,
                vfs_to_data, vfs_to_json, '%s loaded from vfs.zip' % name):
            fail = True

        if from_ref is not None:
            ref_game = from_ref[name]
            ref_game.load_file_data(use_vfs='ref.zip')
            ref_to_data = ref_game.to_data()
            ref_to_json = dump(ref_to_data)

            compare(
                ref_to_data, ref_to_json, '%s loaded from ref.zip' % name,
                vfs_to_data, vfs_to_json, '%s loaded from vfs.zip' % name)

        with open(
            os.path.join(output, '%s-derived.txt' % name), 'w'
        ) as writer:
            writer.write(vfs_to_json)

        with open(
            os.path.join(output, '%s-unexpanded.txt' % name), 'w'
        ) as writer:
            writer.write(json_to_json_unexpanded)

        # JSON is a subset of YAML so we can treat this as if YAML
        json_to_json_game = load_game(
            False, os.path.join(output, '%s-unexpanded.txt' % name),
            None, name=name,
            yaml_file=os.path.join(output, '%s-unexpanded.txt' % name),
        )
        json_to_json_game.load_file_data()

        json_to_json_to_data = json_to_json_game.to_data()
        json_to_json_to_json = dump(json_to_json_to_data)

        if not compare(
            json_to_json_to_data, json_to_json_to_json,
            '%s loaded from unexpanded YAML' % name,
            vfs_to_data, vfs_to_json, '%s loaded from vfs.zip' % name
        ):

            with open(
                os.path.join(output, '%s-unexpanded-yaml.txt' % name), 'w'
            ) as writer:
                writer.write(yaml_dump(json_to_data_unexpanded))

            fail = True

    raise SystemExit(fail)
