# Copyright (c) 2022 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import re
import subprocess
import sys
from typing import Tuple

# The version MUST be of the form `YYYY.M.D'
#
# Use scripts/tools/zap/version_update.py to manage ZAP versioning as many
# files may need updating for versions
#
MIN_ZAP_VERSION = '2023.8.4'


class ZapTool:
    def __init__(self):
        self.check_version = True

        # Accepted environment variables, in order:
        #
        # ZAP_DEVELOPMENT_PATH - the path to a zap development environment. This is
        #                        a zap checkout, used for local development
        # ZAP_INSTALL_PATH     - the path where zap-cli exists. This is if zap-cli
        #                        is NOT in the current path
        if 'ZAP_DEVELOPMENT_PATH' in os.environ:
            self.zap_start = ['node', 'src-script/zap-start.js']
            self.working_directory = os.environ['ZAP_DEVELOPMENT_PATH']
            # When using ZAP_DEVELOPMENT_PATH, we should generally not need to
            # setup a version (dirties the tree and conflicts with parallel
            # execution
            # HOWEVER, if the intent is exactly to check the version, this should not be done
            # and this set should be reverted
            os.environ['ZAP_SKIP_REAL_VERSION'] = '1'
            self.check_version = False  # cannot check versions without dirtying tree
        elif 'ZAP_INSTALL_PATH' in os.environ:
            self.zap_start = [os.path.join(
                os.environ['ZAP_INSTALL_PATH'], 'zap-cli')]
            self.working_directory = None
        else:
            self.zap_start = ['zap-cli']
            self.working_directory = None

    def version_check(self, min_version=None):
        if not min_version:
            min_version = MIN_ZAP_VERSION

        if not self.check_version:
            print("Unable to check version using %r. Assuming version is ok.", self.zap_start)
            return

        try:
            version = ''
            version_re = re.compile(r'Version:\s*(\d+\.\d+\.\d+)')

            for line in subprocess.check_output(self.zap_start + ['--version'], cwd=self.working_directory).splitlines():
                try:
                    m = version_re.match(line.decode('utf-8'))
                    if m:
                        version = m.group(1)
                        break
                except UnicodeDecodeError:
                    # Version line will not be unicode ... skip over odd lines
                    pass

            if not version:
                print(
                    f"Failed to find `Version: ....` line from {self.zap_start} --version")
                sys.exit(1)
        except FileNotFoundError as e:
            print(
                f'FAILED TO EXECUTE ZAP GENERATION: {e.strerror} - "{e.filename}"')
            print('*'*80)
            print('* You may need to install zap. Please ensure one of these applies:')
            print(
                '* - `zap-cli` is in $PATH. Install from https://github.com/project-chip/zap/releases')
            print('*   see docs/guides/BUILDING.md for details')
            print('* - `zap-cli` is in $ZAP_INSTALL_PATH. Use this option if you')
            print('*   installed zap but do not want to update $PATH')
            print('* - Point $ZAP_DEVELOPMENT_PATH to your local copy of zap that you')
            print('*   develop on (to use a developer build of zap)')
            print('*'*80)
            sys.exit(1)

        def parse_version_string(str):
            return list(map(lambda component: int(component), str.split('.')))

        if parse_version_string(version) < parse_version_string(MIN_ZAP_VERSION):
            print(f"Checking ZAP from {self.zap_start}:")
            print(
                f"  !!! Version validation failed: required at least {MIN_ZAP_VERSION}, got {version} instead")
            sys.exit(1)

    def run(self, cmd: str, *extra_args: Tuple[str]):
        try:
            subprocess.check_call(
                self.zap_start + [cmd] + list(extra_args), cwd=self.working_directory)
        except FileNotFoundError as e:
            print(
                f'FAILED TO EXECUTE ZAP GENERATION: {e.strerror} - "{e.filename}"')
            print('*'*80)
            print('* You may need to install zap. Please ensure one of these applies:')
            print(
                '* - `zap-cli` is in $PATH. Install from https://github.com/project-chip/zap/releases')
            print('*   see docs/guides/BUILDING.md for details')
            print('* - `zap-cli` is in $ZAP_INSTALL_PATH. Use this option if you')
            print('*   installed zap but do not want to update $PATH')
            print('* - Point $ZAP_DEVELOPMENT_PATH to your local copy of zap that you')
            print('*   develop on (to use a developer build of zap)')
            print('*'*80)
            sys.exit(1)
