#!/usr/bin/env python3
# -*- coding: latin-1 -*-
"""Get RDS instance support details from AWS accounts."""

import traceback
import itertools
import argparse
import json
import boto3
import aws_crawler
from multithreader import threads
from aws_authenticator import AWSAuthenticator


__version__ = '1.2.1'
__author__ = 'Ahmad Ferdaus Abd Razak'
__application__ = 'rds_support'


def get_enabled_regions(client) -> list:
    """Get all enabled regions for an AWS account."""
    try:
        response = client.describe_regions(AllRegions=True)
        enabled_regions = [
            region.get('RegionName')
            for region in response['Regions']
            if region.get('OptInStatus') in ['opted-in', 'opt-in-not-required']
        ]

    except Exception as e:
        print(f'Error getting regions: {str(e)}')
        enabled_regions = []

    return enabled_regions


def get_rds_support_details(account_id: str, items: dict) -> dict:
    """Get RDS instance support details from an AWS account."""
    print(f'Working on {account_id}...')

    if account_id != items['sso_account_id']:
        try:
            if items.get('assumed_role_name') is not None:
                credentials = aws_crawler.get_credentials(
                    items.get('session'),
                    f'arn:aws:iam::{account_id}:role/{items.get("assumed_role_name")}',
                    items.get('external_id')
                )
                ec2 = boto3.client(
                    'ec2',
                    aws_access_key_id=credentials.get('aws_access_key_id'),
                    aws_secret_access_key=credentials.get('aws_secret_access_key'),
                    aws_session_token=credentials.get('aws_session_token')
                )
            else:
                auth = AWSAuthenticator(
                    sso_url=items.get('sso_url'),
                    sso_role_name=items.get('sso_role_name'),
                    sso_account_id=account_id
                )
                session = auth.sso()
                ec2 = session.client('ec2')
            enabled_regions = get_enabled_regions(ec2)

            instances = []
            for region in enabled_regions:
                if items.get('assumed_role_name') is not None:
                    rds = boto3.client(
                        'rds',
                        aws_access_key_id=credentials.get('aws_access_key_id'),
                        aws_secret_access_key=credentials.get('aws_secret_access_key'),
                        aws_session_token=credentials.get('aws_session_token'),
                        region_name=region
                    )
                else:
                    rds = session.client('rds', region_name=region)

                paginator = rds.get_paginator('describe_db_instances')
                response_iterator = paginator.paginate()

                for page in response_iterator:
                    db_instances = page['DBInstances']
                    if len(db_instances) > 0:
                        for db_instance in db_instances:
                            db_instance_identifier = db_instance['DBInstanceIdentifier']
                            lifecycle_support = db_instance.get('EngineLifecycleSupport')
                            engine = db_instance.get('Engine')
                            engine_version = db_instance.get('EngineVersion')

                            major_engine_version = '.'.join(engine_version.split('.')[:2])
                            major_versions = rds.describe_db_major_engine_versions(
                                Engine=engine,
                                MajorEngineVersion=major_engine_version
                            )
                            support_start = (
                                major_versions
                                .get('DBMajorEngineVersions', [])[0]
                                .get('SupportedEngineLifecycles', [])[0]
                                .get('LifecycleSupportStartDate')
                            )
                            support_end = (
                                major_versions
                                .get('DBMajorEngineVersions', [])[0]
                                .get('SupportedEngineLifecycles', [])[0]
                                .get('LifecycleSupportEndDate')
                            )

                            instances.append(
                                {
                                    'account_id': account_id,
                                    'region': region,
                                    'rds_id': db_instance_identifier,
                                    'support': lifecycle_support,
                                    'engine': engine,
                                    'engine_version': engine_version,
                                    'support_start_date': support_start,
                                    'support_end_date': support_end
                                }
                            )

        except Exception as e:
            tb_list = traceback.extract_tb(e.__traceback__)
            last_frame = tb_list[-1]
            line_number = last_frame.lineno
            instances = [
                {
                    'account_id': account_id,
                    'exception': str(e),
                    'line_number': line_number
                }
            ]

    else:
        instances = [
            {
                'account_id': account_id,
                'exception': 'Skipped master account.'
            }
        ]

    return instances


def get_params():
    """Get parameters from script inputs."""
    myparser = argparse.ArgumentParser(
        add_help=True,
        allow_abbrev=False,
        description="Get RDS instance support details from AWS accounts.",
        usage=f"{__application__} [options]"
    )
    myparser.add_argument(
        "-V", "--version", action="version", version=f"{__application__} {__version__}"
    )
    myparser.add_argument(
        "-a",
        "--sso_account_id",
        action="store",
        help="SSO Account ID.",
        required=True,
        type=str
    )
    myparser.add_argument(
        "-u",
        "--sso_url",
        action="store",
        help="SSO URL.",
        required=True,
        type=str
    )
    myparser.add_argument(
        "-r",
        "--sso_role_name",
        action="store",
        help="SSO Role Name.",
        required=True,
        type=str
    )
    myparser.add_argument(
        "-n",
        "--assumed_role_name",
        action="store",
        help="Assumed Role Name (default: None).",
        nargs="?",
        default=None,
        required=False,
        type=str
    )
    myparser.add_argument(
        "-e",
        "--external_id",
        action="store",
        help="External ID for assumed role (default: None).",
        nargs="?",
        default=None,
        required=False,
        type=str
    )
    myparser.add_argument(
        "-s",
        "--account_statuses",
        action="store",
        help="Comma-separated account statuses to filter (default: None).",
        nargs="?",
        default=None,
        required=False,
        type=str
    )
    myparser.add_argument(
        "-o",
        "--account_ou",
        action="store",
        help="Account Organizational Unit to filter (default: None).",
        nargs="?",
        default=None,
        required=False,
        type=str
    )
    myparser.add_argument(
        "-l",
        "--account_list",
        action="store",
        help="Comma-separated list of account IDs to filter (default: None).",
        nargs="?",
        default=None,
        required=False,
        type=str
    )
    myparser.add_argument(
        "-t",
        "--thread_num",
        action="store",
        help="Number of threads to use for multithreading (default: 10).",
        nargs="?",
        default=10,
        required=False,
        type=int
    )
    return myparser.parse_args()


def main():
    """Execute main function."""
    args = get_params()
    sso_account_id = args.sso_account_id.replace(' ', '')
    sso_url = args.sso_url.replace(' ', '')
    sso_role_name = args.sso_role_name.replace(' ', '')
    thread_num = args.thread_num
    assumed_role_name = (
        args.assumed_role_name.replace(' ', '')
        if args.assumed_role_name is not None else None
    )
    external_id = (
        args.external_id.replace(' ', '')
        if args.external_id is not None else None
    )
    account_statuses = (
        args.account_statuses.replace(' ', '')
        if args.account_statuses is not None else None
    )
    account_ou = (
        args.account_ou.replace(' ', '')
        if args.account_ou is not None else None
    )
    account_list = (
        args.account_list.replace(' ', '')
        if args.account_list is not None else None
    )

    items = {
        'sso_url': sso_url,
        'sso_role_name': sso_role_name,
        'sso_account_id': sso_account_id,
        'assumed_role_name': assumed_role_name,
        'external_id': external_id
    }

    if account_statuses is not None:
        auth = AWSAuthenticator(
            sso_url=sso_url,
            sso_role_name=sso_role_name,
            sso_account_id=sso_account_id
        )
        session = auth.sso()
        account_ids = aws_crawler.list_accounts(session, account_statuses)
        items['session'] = session
        res = threads(get_rds_support_details, account_ids, items, thread_num=thread_num)
        results = list(itertools.chain.from_iterable(res))
        print(json.dumps(results, default=str, indent=2))

    elif account_ou is not None:
        auth = AWSAuthenticator(
            sso_url=sso_url,
            sso_role_name=sso_role_name,
            sso_account_id=sso_account_id
        )
        session = auth.sso()
        account_ids = aws_crawler.list_ou_accounts(session, account_ou)
        items['session'] = session
        res = threads(get_rds_support_details, account_ids, items, thread_num=thread_num)
        results = list(itertools.chain.from_iterable(res))
        print(json.dumps(results, default=str, indent=2))

    elif account_list is not None and assumed_role_name is not None:
        auth = AWSAuthenticator(
            sso_url=sso_url,
            sso_role_name=sso_role_name,
            sso_account_id=sso_account_id
        )
        session = auth.sso()
        account_ids = aws_crawler.create_account_list(account_list)
        items['session'] = session
        res = threads(get_rds_support_details, account_ids, items, thread_num=thread_num)
        results = list(itertools.chain.from_iterable(res))
        print(json.dumps(results, default=str, indent=2))

    elif account_list is not None and assumed_role_name is None:
        account_ids = aws_crawler.create_account_list(account_list)
        res = [get_rds_support_details(account_id, items) for account_id in account_ids]
        results = list(itertools.chain.from_iterable(res))
        print(json.dumps(results, default=str, indent=2))

    else:
        pass
