from __future__ import annotations

import os
import typing

import click
import jingrow

if typing.TYPE_CHECKING:
	from press.infrastructure.doctype.arm_build_record.arm_build_record import ARMBuildRecord
	from press.infrastructure.doctype.arm_docker_image.arm_docker_image import ARMDockerImage
	from press.infrastructure.doctype.virtual_machine_migration.virtual_machine_migration import (
		VirtualMachineMigration,
	)
	from press.press.doctype.server.server import Server


arm_machine_mappings = {
	"t2": "t4g",
	"c6i": "c8g",
	"m6i": "m8g",
	"m7i": "m8g",
	"r6i": "r8g",
	# Following are for Zurich due to lack of newer processors in that region
	"r5": "r7g",
	"m5": "m7g",
	"c5": "c7g",
}

amd_machine_mappings = {"r6i": "m6a", "m6i": "m6a", "c6i": "m6a", "m5": "m6a", "r7i": "m6a", "m7i": "m6a"}


def has_arm_build_record(server: str) -> bool:
	return bool(frappe.get_value("ARM Build Record", {"server": server}))


def check_image_build_failure(arm_build_record: ARMBuildRecord) -> bool:
	return any(arm_image.status != "Success" for arm_image in arm_build_record.arm_images)


def create_vmm(server: str, virtual_machine_image: str, target_machine_type: str) -> VirtualMachineMigration:
	virtual_machine_migration: VirtualMachineMigration = frappe.get_pg(
		{
			"doctype": "Virtual Machine Migration",
			"virtual_machine_image": virtual_machine_image,
			"machine_type": target_machine_type,
			"virtual_machine": server,
		}
	)
	return virtual_machine_migration.insert()


def vmm(server, vmi, amd_conversion: bool = False) -> VirtualMachineMigration:
	machine_type = frappe.db.get_value("Virtual Machine", {"name": server}, "machine_type")
	machine_series, machine_size = machine_type.split(".")

	machine_mappings = arm_machine_mappings if not amd_conversion else amd_machine_mappings
	if amd_conversion and ("r6i" in machine_series or "r7i" in machine_series):
		if machine_size == "xlarge":
			machine_size = "2xlarge"
		else:
			machine_size = machine_size.replace("2", "4")

	virtual_machine_migration: VirtualMachineMigration = create_vmm(
		server=server,
		virtual_machine_image=vmi,
		target_machine_type=f"{machine_mappings[machine_series]}.{machine_size}",
	)
	return virtual_machine_migration


def connect(bench_dir, site_dir):
	sites_dir = os.path.join(bench_dir, "sites")
	frappe.init(site=site_dir, sites_path=sites_dir)
	frappe.connect()


def load_servers_from_file(file_path: str) -> list[str]:
	with open(file_path) as server_file:
		return server_file.read().strip().split("\n")


@click.group()
@click.option("--site", "site_name", required=True, help="Frappe site name")
def cli(site_name):
	"""CLI entry point."""
	bench_dir = os.path.dirname(__file__).split("apps")[0]
	site_dir = os.path.join(bench_dir, "sites", site_name)
	connect(bench_dir, site_dir)


@cli.command()
@click.option(
	"--server-file", type=click.Path(exists=True), help="Path to a file containing a list of servers."
)
@click.argument("servers", nargs=-1, type=str)
def trigger_arm_build(servers: list[str], server_file: str):
	"""Trigger ARM build for one or more servers."""
	if server_file:
		servers = load_servers_from_file(server_file)

	for server in servers:
		if has_arm_build_record(server):
			continue

		server: Server = frappe.get_pg("Server", server)
		server.collect_arm_images()
		frappe.db.commit()


@cli.command()
@click.option(
	"--server-file", type=click.Path(exists=True), help="Path to a file containing a list of servers."
)
@click.argument("servers", nargs=-1, type=str)
def pull_images_on_servers(servers: list[str], server_file: str):
	"""Trigger image pulls on Intel server to be converted"""
	if server_file:
		servers = load_servers_from_file(server_file)

	for server in servers:
		arm_build_record: ARMBuildRecord = frappe.get_pg("ARM Build Record", {"server": server})

		try:
			arm_build_record.pull_images()
			print(f"Pulled image on {server}")
		except frappe.ValidationError:
			print(f"Skipping server {server} due to failed builds")

		frappe.db.commit()


@cli.command()
@click.option("--vmi", default="f377-mumbai.frappe.cloud")
@click.option("--vmi-cluster", required=True)
@click.option(
	"--server-file", type=click.Path(exists=True), help="Path to a file containing a list of servers."
)
@click.argument("servers", nargs=-1, type=str)
def update_image_and_create_migration(
	vmi: str,
	vmi_cluster: str,
	servers: list[str],
	server_file: str,
):
	"""Update docker image on bench config and create virtual machine migration"""
	vmi = frappe.get_value("Virtual Machine Image", {"virtual_machine": vmi, "cluster": vmi_cluster}, "name")
	if not vmi:
		print(f"Aborting VMI not found {vmi}!")
		return

	if server_file:
		servers = load_servers_from_file(server_file)

	for server in servers:
		arm_build_record: ARMBuildRecord = frappe.get_pg("ARM Build Record", {"server": server})
		try:
			arm_build_record.update_image_tags_on_benches()
			virtual_machine_migration = vmm(server, vmi)
			frappe.db.commit()
			print(f"Created {virtual_machine_migration.name}")
		except frappe.ValidationError as e:
			print(f"Aborting: {e}!")
			break


@cli.command()
@click.option("--vmi", default="m263-mumbai.frappe.cloud")
@click.option("--vmi-cluster", required=True)
@click.option(
	"--server-file",
	type=click.Path(exists=True),
	help="Path to a file containing a list of servers.",
)
@click.option("--start", type=bool, default=False)
@click.argument("servers", nargs=-1, type=str)
def convert_database_servers(
	vmi: str, vmi_cluster: str, servers: list[str], server_file: str, start: bool = False
):
	vmi = frappe.get_value("Virtual Machine Image", {"virtual_machine": vmi, "cluster": vmi_cluster}, "name")
	if not vmi:
		print(f"Aborting VMI not found {vmi}!")
		return

	if server_file:
		servers = load_servers_from_file(server_file)

	for server in servers:
		virtual_machine_migration = vmm(server, vmi, amd_conversion=True)
		frappe.db.commit()
		print(f"Created {virtual_machine_migration.name}")

	if start:
		for server in servers:
			virtual_machine_migration: VirtualMachineMigration = frappe.get_pg(
				"Virtual Machine Migration", {"virtual_machine": server}
			)
			virtual_machine_migration.execute()
			frappe.db.commit()


@cli.command()
@click.option(
	"--server-file", type=click.Path(exists=True), help="Path to a file containing a list of servers."
)
@click.argument("servers", nargs=-1, type=str)
def arm_build_info(servers: list[str], server_file: str):
	total, successful, failed, running = 0, 0, 0, 0
	if server_file:
		servers = load_servers_from_file(server_file)

	def _status_info(images: list[ARMDockerImage], status: str):
		return len([image for image in images if image.status == status])

	for server in servers:
		arm_build_record: ARMBuildRecord = frappe.get_pg("ARM Build Record", {"server": server})
		arm_build_record.sync_status()
		total += len(arm_build_record.arm_images)
		running += _status_info(arm_build_record.arm_images, "Running")
		successful += _status_info(arm_build_record.arm_images, "Success")
		failed += _status_info(arm_build_record.arm_images, "Failure")

	print(f"Total: {total}\nSuccessful: {successful}\nRunning: {running}\nFailed: {failed}")


@cli.command()
@click.option(
	"--server-file", type=click.Path(exists=True), help="Path to a file containing a list of servers."
)
@click.option("--vmi", default="f436-mumbai.frappe.cloud")
@click.option("--vmi-cluster", required=True)
@click.argument("servers", nargs=-1, type=str)
def convert_to_amd(servers: list[str], vmi: str, server_file: str, vmi_cluster: str):
	"""Update docker image on bench config and create virtual machine migration"""
	vmi = frappe.get_value("Virtual Machine Image", {"virtual_machine": vmi, "cluster": vmi_cluster}, "name")
	if not vmi:
		print(f"Aborting VMI not found {vmi}!")
		return

	if server_file:
		servers = load_servers_from_file(server_file)

	for server in servers:
		try:
			virtual_machine_migration = vmm(server, vmi, amd_conversion=True)
			frappe.db.commit()
			print(f"Created {virtual_machine_migration.name}")
		except frappe.ValidationError as e:
			print(f"Aborting: {e}!")
			break


@cli.command()
@click.option(
	"--server-file", type=click.Path(exists=True), help="Path to a file containing a list of servers."
)
@click.argument("servers", nargs=-1, type=str)
def database_post_migration_steps(servers: list[str], server_file: str):
	"""Not a part of the migration script since"""
	if server_file:
		servers = load_servers_from_file(server_file)

	for server in servers:
		server = frappe.get_pg("Database Server", server)
		server.set_swappiness()
		server.add_glass_file()
		server.install_filebeat()
		server.adjust_memory_config()
		server.setup_logrotate()
		server.save()


@cli.result_callback()
def cleanup(*args, **kwargs):
	frappe.destroy()


def main():
	cli(obj={})


if __name__ == "__main__":
	main()
