# Released under the MIT License. See LICENSE for details.
#
"""Fetch and cache xcode project build paths.

This saves the few seconds it normally would take to fire up xcodebuild
and filter its output.
"""

from __future__ import annotations

import json
import os
import subprocess
import sys
import time
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from typing import Dict, Any, Optional


def project_build_path(projroot: str, project_path: str,
                       configuration: str) -> str:
    """Main script entry point."""
    # pylint: disable=too-many-locals

    config_path = os.path.join(projroot, '.cache', 'xcode_build_path')
    out_path = None
    config: Dict[str, Dict[str, Any]] = {}

    build_dir: Optional[str] = None

    try:
        if os.path.exists(config_path):
            with open(config_path, encoding='utf-8') as infile:
                config = json.loads(infile.read())
            if (project_path in config
                    and configuration in config[project_path]):

                # Ok we've found a build-dir entry for this project; now if it
                # exists on disk and all timestamps within it are decently
                # close to the one we've got recorded, lets use it.
                # (Anything using this script should also be building
                # stuff there so mod times should be pretty recent; if not
                # then its worth re-caching to be sure.)
                build_dir = config[project_path][configuration]['build_dir']
                timestamp = config[project_path][configuration]['timestamp']
                assert build_dir is not None
                if os.path.isdir(build_dir):
                    use_cached = True

                    # if its been over a day since we cached this, renew it
                    now = time.time()
                    if abs(now - timestamp) > 60 * 60 * 24:
                        use_cached = False

                    if use_cached:
                        out_path = build_dir
    except Exception:
        import traceback
        print('EXCEPTION checking cached build path', file=sys.stderr)
        traceback.print_exc()
        out_path = None

    # If we don't have a path at this point we look it up and cache it.
    if out_path is None:
        print('Caching xcode build path...', file=sys.stderr)
        output = subprocess.check_output([
            'xcodebuild', '-project', project_path, '-showBuildSettings',
            '-configuration', configuration
        ]).decode('utf-8')
        prefix = 'TARGET_BUILD_DIR = '
        lines = [
            l for l in output.splitlines() if l.strip().startswith(prefix)
        ]
        if len(lines) != 1:
            raise Exception(
                'TARGET_BUILD_DIR not found in xcodebuild settings output')
        build_dir = lines[0].replace(prefix, '').strip()
        if project_path not in config:
            config[project_path] = {}
        config[project_path][configuration] = {
            'build_dir': build_dir,
            'timestamp': time.time()
        }
        os.makedirs(os.path.dirname(config_path), exist_ok=True)
        with open(config_path, 'w', encoding='utf-8') as outfile:
            outfile.write(json.dumps(config))

    assert build_dir is not None
    return build_dir
