# -*- coding: utf-8 -*- #
# Copyright 2021 Google LLC. All Rights Reserved.
#
# 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.
"""Module containing the command utils for resource-config/terraform surface."""

from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

import os
import re

from googlecloudsdk.calliope.exceptions import core_exceptions
from googlecloudsdk.core.util import files
from googlecloudsdk.core.util import platforms
from googlecloudsdk.core.util import times

IMPORT_REGEX = re.compile(r'^#')
_IMPORT_CMD_PREFIX = 'terraform import'
IMPORT_SCRIPT_DEFAULT_NAME = 'terraform_import_{ts}.{suffix}'
IMPORT_DATE_FORMAT = '%Y%m%d-%H-%M-%S'
SHELLSCRIPT_TEMPLATES = {
    'WINDOWS': """\
@echo off
REM Terraform Import Script generated by gcloud cli

{data}
""",
    'BASH': """\
#!/bin/sh
# Terraform Import Script generated by gcloud cli

{data}
"""
}

INVALID_EXPORT_PATH_MSG = ('Invalid export Path {}. Path should point to an '
                           'export Terraform(.tf) file or directotry of '
                           'Terraform files.\nPlease see `gcloud alpha '
                           'resource-config bulk-export` for more details.')


class TerraformGenerationError(core_exceptions.Error):
  """Base Exception for errors that occur during import script generation."""


def ParseExportFiles(export_path):
  """Read files from export path and parse out import command statements."""
  # Test for valid path -> Raise FNF on error
  if os.path.isfile(export_path) and export_path.endswith('.tf'):
    input_files = [export_path]
  elif os.path.isdir(export_path):
    input_files = files.GetDirectoryTreeListing(
        export_path, include_dirs=False,
        file_predicate=lambda x: x.endswith('.tf'))
  else:
    raise ValueError(INVALID_EXPORT_PATH_MSG)
  import_data = []
  error_files = []
  for in_file in input_files:
    import_reader = files.FilteredFileReader(in_file, IMPORT_REGEX)
    try:
      # Tuple of (file_path, import command statement)
      command = list(import_reader).pop()
      import_line = (files.ExpandHomeAndVars(os.path.dirname(in_file)),
                     command.partition('#')[2].strip())
      import_data.append(import_line)
    except IndexError:
      error_files.append(in_file)
    except files.Error as e:
      raise TerraformGenerationError(
          'Could not parse Terrorm data from {path}:: {err}'.format(
              path=export_path, err=e))

  return import_data, error_files


def _GenerateDefaultFileName():
  """Generate a default filename for import script."""
  suffix = 'cmd' if platforms.OperatingSystem.IsWindows() else 'sh'
  return IMPORT_SCRIPT_DEFAULT_NAME.format(
      ts=times.FormatDateTime(times.Now(), IMPORT_DATE_FORMAT), suffix=suffix)


def GenerateScriptFromTemplate(import_path, dest_file=None, dest_dir=None):
  """Generate Terraform import shell script from template.

  Args:
    import_path: string, Path to exported terraform file(s).
    dest_file: string, Filename path to write the generated script to. If
     dest_file is None, then a default filename will be generated.
    dest_dir: string, Directory path to write the generated script to. If
      dest_dir is None, then script will be written to CWD.

  Returns:
    tuple(string, int, [string])), the path to the generated script, number of
      import statements generated and list of files that could not be processed.

  Raises:
    TerraformGenerationError: If and error occurs writing to disk/stdout.
  """
  import_data, error_files = ParseExportFiles(import_path)
  if not import_data:
    return None, 0, error_files

  dest_dir = dest_dir or files.GetCWD()
  if os.path.isdir(dest_dir) and not files.HasWriteAccessInDir(dest_dir):
    raise TerraformGenerationError(
        'Error writing import script: {} is not writable'.format(dest_dir))

  dest_file = dest_file or _GenerateDefaultFileName()
  output_file_name = os.path.join(dest_dir, dest_file)
  contents = []
  for import_path, import_statement in import_data:
    import_cmd_data = import_statement.partition(_IMPORT_CMD_PREFIX)[1:]
    contents.append('{cmd} -config={flag_path} {cmd_sfx}'.format(
        cmd=import_cmd_data[0],
        flag_path=import_path,
        cmd_sfx=import_cmd_data[1]))
  template_key = 'WINDOWS' if platforms.OperatingSystem.IsWindows() else 'BASH'

  final_script = SHELLSCRIPT_TEMPLATES[template_key].format(
      data=os.linesep.join(contents))
  try:
    files.WriteFileContents(output_file_name,
                            overwrite=True,
                            contents=final_script,
                            create_path=True)
    os.chmod(output_file_name, 0o755)
  except files.Error as e:
    raise TerraformGenerationError('Error writing import script::{}'.format(e))

  return output_file_name, len(import_data), error_files
