#!/usr/bin/env python3
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import plistlib
import os
import xml.etree.ElementTree as ET
import argparse

# --- Configuration ---
GRD_FILENAME = "strings/ios_credential_provider_extension_strings.grd"
PLIST_FILENAME = "credential_provider_extension_localize_strings_config.plist"
BASE_FILENAME = "generated_localized_strings"
HEADER_FILENAME = BASE_FILENAME + ".h"
SOURCE_FILENAME = BASE_FILENAME + ".mm"

HEADER_GUARD = "IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_GENERATED_LOCALIZED_STRINGS_H_"
LICENSE_HEADER = """// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
"""
AUTOGENERATED_NOTE = """
/// NOTE: THIS FILE IS AUTOGENERATED. DO NOT MODIFY MANUALLY.
// To update, please run (in ios/chrome/credential_provider_extension/):
// python3 generate_localized_strings.py
"""
PREFIX_TO_REMOVE = "IDS_IOS_"

HELP_MESSAGE = """
Here's how to add new strings to the Credential Provider Extension:
1) Add your strings to strings/ios_credential_provider_extension_strings.grd
2) Follow the instructions to add screenshots (see https://chromium.googlesource.com/chromium/src/+/main/docs/translation_screenshots.md)
3) Run python3 generate_localized_strings.py
4) Import generated_localized_strings.h in your code
5) Your string is accessible using a helper function. If your new string is called IDS_IOS_MY_STRING_NAME, you can access your string using MyStringNameString().
"""

# --- Utility Functions ---

def snake_to_camel(snake_str):
    """
    Converts snake_case to CamelCase, removes prefix, and ensures
    the function name ends with "String".
    """
    if snake_str.startswith(PREFIX_TO_REMOVE):
        cleaned_str = snake_str[len(PREFIX_TO_REMOVE):]
    else:
        cleaned_str = snake_str

    components = cleaned_str.split('_')
    camel_name = "".join(word.capitalize() for word in components)

    # Ensure the name ends with "String"
    if not camel_name.endswith("String"):
        camel_name += "String"

    return camel_name


def extract_strings_from_grd(grd_filepath):
    print(f"1. Parsing GRD file: {grd_filepath}")
    extracted_strings = []
    if not os.path.exists(grd_filepath):
        return extracted_strings
    try:
        tree = ET.parse(grd_filepath)
        root = tree.getroot()
        for msg in root.findall(".//message"):
            name = msg.get('name')
            if name and name.startswith("IDS_"):
                extracted_strings.append(name)
    except Exception as e:
        print(f"Error parsing GRD file: {e}")
    return extracted_strings


def generate_plist_file(strings_array, plist_filepath):
    print(f"2. Generating Plist file: {plist_filepath}")
    plist_data = {
        "headers": [
            "ios/credential_provider_extension/grit/ios_credential_provider_extension_strings.h"
        ],
        "outputs": [{
            "name": "Localizable.strings",
            "strings": strings_array
        }]
    }
    try:
        os.makedirs(os.path.dirname(plist_filepath) or '.', exist_ok=True)
        with open(plist_filepath, 'wb') as fp:
            plistlib.dump(plist_data, fp)
        return True
    except Exception as e:
        print(f"Error writing plist: {e}")
        return False


def generate_accessor_files(plist_filepath):
    print(
        f"3. Generating Accessor files: {HEADER_FILENAME} and {SOURCE_FILENAME}"
    )
    try:
        with open(plist_filepath, 'rb') as fp:
            data = plistlib.load(fp)
    except Exception as e:
        return

    localized_strings = data.get("outputs", [{}])[0].get("strings", [])

    # --- Header File Content ---
    header_content = [
        LICENSE_HEADER,
        f"#ifndef {HEADER_GUARD}\n#define {HEADER_GUARD}\n",
        "#import <Foundation/Foundation.h>\n",
        AUTOGENERATED_NOTE,
        "\n// clang-format off\n",
    ]

    # --- Source File Content ---
    source_content = [
        LICENSE_HEADER,
        f"#import \"{HEADER_FILENAME}\"\n",
        AUTOGENERATED_NOTE,
        "\n// clang-format off\n",
    ]

    for original_string in localized_strings:
        func_name = snake_to_camel(original_string)
        header_content.append(f"NSString* {func_name}();\n")

        source_content.append(f"NSString* {func_name}() {{\n")
        source_content.append(
            f"  return NSLocalizedString(@\"{original_string}\", @\"\");\n")
        source_content.append("}\n\n")

    header_content.append("// clang-format on\n")
    header_content.append(f"\n#endif  // {HEADER_GUARD}\n")

    source_content.append("// clang-format on\n")

    with open(HEADER_FILENAME, 'w') as f:
        f.write("".join(header_content))
    with open(SOURCE_FILENAME, 'w') as f:
        f.write("".join(source_content))


# --- Main ---

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Generates C++ accessor functions for localized strings.",
        formatter_class=argparse.RawTextHelpFormatter,
        add_help=False)
    parser.add_argument('--help', action='store_true')
    args = parser.parse_args()

    if args.help:
        print(HELP_MESSAGE)
        exit(0)

    strings = extract_strings_from_grd(GRD_FILENAME)
    if strings and generate_plist_file(strings, PLIST_FILENAME):
        generate_accessor_files(PLIST_FILENAME)
        print("\n--- Process Complete ---")
