#!/usr/pkg/bin/python3.13

import argparse
import locale
import os
from collections.abc import Mapping

from jinja2 import Environment, FileSystemLoader

try:
    import zoneinfo
except ModuleNotFoundError:
    from backports import zoneinfo

try:
    import readline  # NOQA
except ImportError:
    pass

try:
    import tzlocal

    if hasattr(tzlocal.get_localzone(), "zone"):
        _DEFAULT_TIMEZONE = tzlocal.get_localzone().zone
    else:
        _DEFAULT_TIMEZONE = tzlocal.get_localzone_name()
except ModuleNotFoundError:
    _DEFAULT_TIMEZONE = "Europe/Rome"

from pelican import __version__

locale.setlocale(locale.LC_ALL, "")
try:
    _DEFAULT_LANGUAGE = locale.getlocale()[0]
except ValueError:
    # Don't fail on macosx: "unknown locale: UTF-8"
    _DEFAULT_LANGUAGE = None
if _DEFAULT_LANGUAGE is None:
    _DEFAULT_LANGUAGE = "en"
else:
    _DEFAULT_LANGUAGE = _DEFAULT_LANGUAGE.split("_")[0]

_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates")
_jinja_env = Environment(
    loader=FileSystemLoader(_TEMPLATES_DIR),
    trim_blocks=True,
    keep_trailing_newline=True,
)


_GITHUB_PAGES_BRANCHES = {"personal": "main", "project": "gh-pages"}

CONF = {
    "pelican": "pelican",
    "pelicanopts": "",
    "basedir": os.curdir,
    "ftp_host": "localhost",
    "ftp_user": "anonymous",
    "ftp_target_dir": "/",
    "ssh_host": "localhost",
    "ssh_port": 22,
    "ssh_user": "root",
    "ssh_target_dir": "/var/www",
    "s3_bucket": "my_s3_bucket",
    "cloudfiles_username": "my_rackspace_username",
    "cloudfiles_api_key": "my_rackspace_api_key",
    "cloudfiles_container": "my_cloudfiles_container",
    "dropbox_dir": "~/Dropbox/Public/",
    "github_pages_branch": _GITHUB_PAGES_BRANCHES["project"],
    "default_pagination": 10,
    "siteurl": "",
    "lang": _DEFAULT_LANGUAGE,
    "timezone": _DEFAULT_TIMEZONE,
}

# url for list of valid timezones
_TZ_URL = "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones"


# Create a 'marked' default path, to determine if someone has supplied
# a path on the command-line.
class _DEFAULT_PATH_TYPE(str):  # noqa: SLOT000
    is_default_path = True


_DEFAULT_PATH = _DEFAULT_PATH_TYPE(os.curdir)


def ask(question, answer=str, default=None, length=None):
    if answer is str:
        r = ""
        while True:
            if default:
                r = input(f"> {question} [{default}] ")
            else:
                r = input(f"> {question} ")

            r = r.strip()

            if len(r) <= 0:
                if default:
                    r = default
                    break
                else:
                    print("You must enter something")
            elif length and len(r) != length:
                print(f"Entry must be {length} characters long")
            else:
                break

        return r

    elif answer is bool:
        r = None
        while True:
            if default is True:
                r = input(f"> {question} (Y/n) ")
            elif default is False:
                r = input(f"> {question} (y/N) ")
            else:
                r = input(f"> {question} (y/n) ")

            r = r.strip().lower()

            if r in ("y", "yes"):
                r = True
                break
            elif r in ("n", "no"):
                r = False
                break
            elif not r:
                r = default
                break
            else:
                print("You must answer 'yes' or 'no'")
        return r
    elif answer is int:
        r = None
        while True:
            if default:
                r = input(f"> {question} [{default}] ")
            else:
                r = input(f"> {question} ")

            r = r.strip()

            if not r:
                r = default
                break

            try:
                r = int(r)
                break
            except ValueError:
                print("You must enter an integer")
        return r
    else:
        raise NotImplementedError("Argument `answer` must be str, bool, or integer")


def ask_timezone(question, default, tzurl):
    """Prompt for time zone and validate input"""
    tz_dict = {tz.lower(): tz for tz in zoneinfo.available_timezones()}
    while True:
        r = ask(question, str, default)
        r = r.strip().replace(" ", "_").lower()
        if r in tz_dict.keys():
            r = tz_dict[r]
            break
        else:
            print(f"Please enter a valid time zone:\n (check [{tzurl}])")
    return r


def render_jinja_template(tmpl_name: str, tmpl_vars: Mapping, target_path: str):
    try:
        with open(
            os.path.join(CONF["basedir"], target_path), "w", encoding="utf-8"
        ) as fd:
            _template = _jinja_env.get_template(tmpl_name)
            fd.write(_template.render(**tmpl_vars))
    except OSError as e:
        print(f"Error: {e}")


def main():
    parser = argparse.ArgumentParser(
        description="A kickstarter for Pelican",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    )
    parser.add_argument(
        "-p", "--path", default=_DEFAULT_PATH, help="The path to generate the blog into"
    )
    parser.add_argument(
        "-t", "--title", metavar="title", help="Set the title of the website"
    )
    parser.add_argument(
        "-a", "--author", metavar="author", help="Set the author name of the website"
    )
    parser.add_argument(
        "-l", "--lang", metavar="lang", help="Set the default web site language"
    )

    args = parser.parse_args()

    print(
        f"""Welcome to pelican-quickstart v{__version__}.

This script will help you create a new Pelican-based website.

Please answer the following questions so this script can generate the files
needed by Pelican.

    """
    )

    project = os.path.join(os.environ.get("VIRTUAL_ENV", os.curdir), ".project")
    no_path_was_specified = hasattr(args.path, "is_default_path")
    if os.path.isfile(project) and no_path_was_specified:
        CONF["basedir"] = open(project).read().rstrip("\n")
        print(
            "Using project associated with current virtual environment. "
            "Will save to:\n{}\n".format(CONF["basedir"])
        )
    else:
        CONF["basedir"] = os.path.abspath(
            os.path.expanduser(
                ask(
                    "Where do you want to create your new web site?",
                    answer=str,
                    default=args.path,
                )
            )
        )

    CONF["sitename"] = ask(
        "What will be the title of this web site?", answer=str, default=args.title
    )
    CONF["author"] = ask(
        "Who will be the author of this web site?", answer=str, default=args.author
    )
    CONF["lang"] = ask(
        "What will be the default language of this web site?",
        str,
        args.lang or CONF["lang"],
        2,
    )

    if ask(
        "Do you want to specify a URL prefix? e.g., https://example.com  ",
        answer=bool,
        default=True,
    ):
        CONF["siteurl"] = ask(
            "What is your URL prefix? (see above example; no trailing slash)",
            str,
            CONF["siteurl"],
        )

    CONF["with_pagination"] = ask(
        "Do you want to enable article pagination?",
        bool,
        bool(CONF["default_pagination"]),
    )

    if CONF["with_pagination"]:
        CONF["default_pagination"] = ask(
            "How many articles per page do you want?",
            int,
            CONF["default_pagination"],
        )
    else:
        CONF["default_pagination"] = False

    CONF["timezone"] = ask_timezone(
        "What is your time zone?", CONF["timezone"], _TZ_URL
    )

    automation = ask(
        "Do you want to generate a tasks.py/Makefile "
        "to automate generation and publishing?",
        bool,
        True,
    )

    if automation:
        if ask(
            "Do you want to upload your website using FTP?", answer=bool, default=False
        ):
            CONF["ftp"] = (True,)
            CONF["ftp_host"] = ask(
                "What is the hostname of your FTP server?", str, CONF["ftp_host"]
            )
            CONF["ftp_user"] = ask(
                "What is your username on that server?", str, CONF["ftp_user"]
            )
            CONF["ftp_target_dir"] = ask(
                "Where do you want to put your web site on that server?",
                str,
                CONF["ftp_target_dir"],
            )
        if ask(
            "Do you want to upload your website using SSH?", answer=bool, default=False
        ):
            CONF["ssh"] = (True,)
            CONF["ssh_host"] = ask(
                "What is the hostname of your SSH server?", str, CONF["ssh_host"]
            )
            CONF["ssh_port"] = ask(
                "What is the port of your SSH server?", int, CONF["ssh_port"]
            )
            CONF["ssh_user"] = ask(
                "What is your username on that server?", str, CONF["ssh_user"]
            )
            CONF["ssh_target_dir"] = ask(
                "Where do you want to put your web site on that server?",
                str,
                CONF["ssh_target_dir"],
            )

        if ask(
            "Do you want to upload your website using Dropbox?",
            answer=bool,
            default=False,
        ):
            CONF["dropbox"] = (True,)
            CONF["dropbox_dir"] = ask(
                "Where is your Dropbox directory?", str, CONF["dropbox_dir"]
            )

        if ask(
            "Do you want to upload your website using S3?", answer=bool, default=False
        ):
            CONF["s3"] = (True,)
            CONF["s3_bucket"] = ask(
                "What is the name of your S3 bucket?", str, CONF["s3_bucket"]
            )

        if ask(
            "Do you want to upload your website using Rackspace Cloud Files?",
            answer=bool,
            default=False,
        ):
            CONF["cloudfiles"] = (True,)
            CONF["cloudfiles_username"] = ask(
                "What is your Rackspace Cloud username?",
                str,
                CONF["cloudfiles_username"],
            )
            CONF["cloudfiles_api_key"] = ask(
                "What is your Rackspace Cloud API key?",
                str,
                CONF["cloudfiles_api_key"],
            )
            CONF["cloudfiles_container"] = ask(
                "What is the name of your Cloud Files container?",
                str,
                CONF["cloudfiles_container"],
            )

        if ask(
            "Do you want to upload your website using GitHub Pages?",
            answer=bool,
            default=False,
        ):
            CONF["github"] = (True,)
            if ask(
                "Is this your personal page (username.github.io)?",
                answer=bool,
                default=False,
            ):
                CONF["github_pages_branch"] = _GITHUB_PAGES_BRANCHES["personal"]
            else:
                CONF["github_pages_branch"] = _GITHUB_PAGES_BRANCHES["project"]

    try:
        os.makedirs(os.path.join(CONF["basedir"], "content"))
    except OSError as e:
        print(f"Error: {e}")

    try:
        os.makedirs(os.path.join(CONF["basedir"], "output"))
    except OSError as e:
        print(f"Error: {e}")

    conf_python = {}
    for key, value in CONF.items():
        conf_python[key] = repr(value)
    render_jinja_template("pelicanconf.py.jinja2", conf_python, "pelicanconf.py")

    render_jinja_template("publishconf.py.jinja2", CONF, "publishconf.py")

    if automation:
        render_jinja_template("tasks.py.jinja2", CONF, "tasks.py")
        render_jinja_template("Makefile.jinja2", CONF, "Makefile")

    print("Done. Your new project is available at {}".format(CONF["basedir"]))


if __name__ == "__main__":
    main()
