import json
import os
import subprocess
import time
from celilo.reporting.stream_report import StreamReport
from celilo.reporting.settings_parser import parse_settings, parse_info_command


class StreamReportGenerator:
    def __init__(self, report_start_time, duration_minutes, package):
        # package is the package name of the app that is being tested e.g  com.somewearlabs.atak.debug
        self.package = package
        self.report_start_time = report_start_time
        self.duration_minutes = duration_minutes
        self.reports = []

    def pull_reports(self, device, package="com.atakmap.app.civ"):
        """
        Pull reports from an Android device and generate a stream report.

        Args:
            device: Android device serial number
            package: Package name of the app (default: com.atakmap.app.civ)

        Returns:
            Tuple containing (image_file_name, settings_payload, info_command, events)

        Raises:
            RuntimeError: If adb commands fail or no report files are found
        """
        reports_dir = 'files/reports'
        tar_file = 'atak_reports.tar'

        try:
            # Clean up existing files/reports directory if it exists
            if os.path.exists('files'):
                print(f"Removing existing 'files' directory")
                result = subprocess.run(['rm', '-rf', 'files'], check=True, capture_output=True, text=True)

            # Pull reports from device using adb
            print(f"Pulling reports from device {device} (package: {package})")
            adb_cmd = ['adb', '-s', device, 'exec-out', 'run-as', package, 'tar', 'c', 'files/reports']

            with open(tar_file, 'wb') as tar_output:
                result = subprocess.run(adb_cmd, stdout=tar_output, stderr=subprocess.PIPE, check=True)

            if not os.path.exists(tar_file) or os.path.getsize(tar_file) == 0:
                raise RuntimeError(f"Failed to pull reports from device {device}: tar file is empty or missing")

            print(f"Successfully pulled reports tar file ({os.path.getsize(tar_file)} bytes)")

            # Extract tar file
            print("Extracting report files")
            result = subprocess.run(['tar', 'xopf', tar_file], check=True, capture_output=True, text=True)

            # Verify the reports directory exists after extraction
            if not os.path.exists(reports_dir):
                raise RuntimeError(f"Reports directory '{reports_dir}' not found after extraction")

            # List and sort report files
            files = os.listdir(reports_dir)
            if not files:
                raise RuntimeError(f"No report files found in {reports_dir}")

            files.sort()
            last_file = files[-1]

            print(f"Found {len(files)} report file(s)")
            print(f"Processing latest report: {last_file}")

            # Copy the latest report file to current directory
            source_path = os.path.join(reports_dir, last_file)
            dest_path = last_file

            result = subprocess.run(['cp', source_path, dest_path], check=True, capture_output=True, text=True)

            if not os.path.exists(dest_path):
                raise RuntimeError(f"Failed to copy report file {last_file}")

            # Generate report
            print(f"Generating stream report for {last_file}")
            report_generator = StreamReport(last_file, device)
            return report_generator.plot()

        except subprocess.CalledProcessError as e:
            error_msg = f"Command failed: {' '.join(e.cmd)}"
            if e.stderr:
                error_msg += f"\nError: {e.stderr.decode() if isinstance(e.stderr, bytes) else e.stderr}"
            raise RuntimeError(error_msg) from e
        except FileNotFoundError as e:
            raise RuntimeError(f"File not found: {e}") from e
        except Exception as e:
            raise RuntimeError(f"Unexpected error while pulling reports: {e}") from e

    def collect_latency_reports(self):
        """
        Collect latency reports from all connected Android devices.

        Returns:
            JSON string containing reports from all devices

        Raises:
            RuntimeError: If no devices are connected or adb command fails
        """
        try:
            # Get list of connected devices
            result = subprocess.run(
                ['adb', 'devices'],
                capture_output=True,
                text=True,
                check=True
            )

            # Parse device list (skip header line, extract just device IDs)
            lines = result.stdout.strip().split('\n')[1:]  # Skip "List of devices attached"
            devices = [line.split()[0] for line in lines if line.strip() and '\tdevice' in line]

            if not devices:
                raise RuntimeError("No Android devices connected. Please connect a device and try again.")

            print(f"Found {len(devices)} connected device(s): {', '.join(devices)}")

            # Collect reports from each device
            for device in devices:
                print(f"\n{'='*60}")
                print(f"Processing device: {device}")
                print(f"{'='*60}")

                try:
                    obj = self.pull_reports(device, package=self.package)
                    image_file_name = obj[0]
                    settings_payload = parse_settings(obj[1])
                    info_command = parse_info_command(obj[2])
                    events = obj[3]
                    events["android_serial"] = device

                    self.reports.append((image_file_name, settings_payload, info_command, events, device))
                    print(f"Successfully processed device {device}")

                except Exception as e:
                    print(f"ERROR: Failed to process device {device}: {e}")
                    # Continue with other devices instead of failing completely
                    continue

            if not self.reports:
                raise RuntimeError("Failed to collect reports from any device")

            # Write reports to JSON file
            reports_json = get_json(self.reports)
            with open("reports.json", "w") as write_file:
                write_file.write(reports_json)

            print(f"\nSuccessfully collected reports from {len(self.reports)} device(s)")
            print(f"Reports saved to: reports.json")

            return reports_json

        except subprocess.CalledProcessError as e:
            error_msg = f"adb command failed: {e.stderr if e.stderr else str(e)}"
            raise RuntimeError(error_msg) from e
        except Exception as e:
            raise RuntimeError(f"Failed to collect latency reports: {e}") from e

    def generate_html(self):
        time_ms = int(time.time() * 1000)
        if not os.path.exists("reports"):
            os.mkdir("reports")

        file_name = f"reports/report_{time_ms}"
        os.mkdir(file_name)

        html = "<html><body>"
        # make the report start time and duration more readable

        html += f'<div style="font-size: 20px; font-weight: bold;">Report Start Time: {self.report_start_time}</div>'
        html += f'<div style="font-size: 20px; font-weight: bold;">Duration: {self.duration_minutes} minutes</div>'

        for report in self.reports:
            image = report[0]
            settings = report[1]
            info_command = report[2]
            events = report[3]
            # print(settings)

            os.system(f"cp {image} {file_name}")
            html += f'<div>{image}</div>'

            if settings is not None:
                html += f'<div>Settings Payload is None</div>'
                html += f'<div>Backhaul:{settings["backhaul_enabled"]}</div>'
                html += f'<div>Connection Mode:{settings["connection_mode"]}</div>'
                html += f'<div>Radio Mode:{settings["radio_mode"]}</div>'
                html += f'<div>Sat Disabled:{settings["sat_disabled"]}</div>'
                html += f'<div>GPS Interval:{settings["gpsInterval"]}</div>'
                html += f'<div>Tracking Interval:{settings["trackingInterval"]}</div>'
            #     frequency
                html += f'<div>Frequency (low):{settings["low_speed_frequency_hz"]}</div>'
                html += f'<div>Frequency (high):{settings["high_speed_frequency_hz"]}</div>'


            if events is not None:
                html += f'<div>Events</div>'
                # event_json = json.loads(events)
                for key in events:
                    html += f'<div>{key} : {events[key]}</div>'

            if info_command is not None:
                html += f'<div>IMEI:{info_command["imei"]}</div>'
                html += f'<div>Serial:{info_command["serial"]}</div>'
                html += f'<div>Firmware Version:{info_command["firmwareVersion"]}</div>'

            html += f'<img src="{image}" style="padding: 10px;" />'

        html += "</body></html>"
        with open(f"{file_name}/report.html", "w") as f:
            f.write(html)

        os.system(f"open {file_name}/report.html")

    def printReport(self):
        json_ob = get_json(generator.reports)
        self.print_formatted_report(json.loads(json_ob))

    def print_formatted_report(self, json):
        print("=== Report ===")
        print("json: ", json)
        # for report in json:
        #     info = report.get('info', {})
        #     serial = info.get('serial', 'N/A')
        #     imei = info.get('imei', 'N/A')
        #
        #     print(f"\n--- Report for Device ---")
        #     print(f"  Serial: {serial}")
        #     print(f"  IMEI: {imei}")
        #
        #     settings = report.get('settings', {})
        #     print("\n  Settings:")
        #     for key, value in settings.items():
        #         print(f"    {key}: {value}")
        #
        #     info = report.get('info', {})
        #     print("\n  Info:")
        #     for key, value in info.items():
        #         print(f"    {key}: {value}")
        #
        #     events = report.get('events', {})
        #     print("\n  Events:")
        #     for key, value in events.items():
        #         print(f"    {key}: {value}")
        #     # Uncomment if event details are needed
        #     # print("\n  Events:")
        #     # for key, value in events.items():
        #     #     print(f"    {key}: {value}")


def get_json(reports):
    print("Generating JSON report from collected data")
    print(f"{reports}")
    """

    :param reports:
    :return:
    """
    report_json = {

    }
    for report in reports:
        image = report[0]
        settings_payload = report[1]
        info_command = report[2]
        events = report[3]
        android_serial = report[4]

        report_json[android_serial] = {"image": image,
                                       "settings": settings_payload,
                                       "info": info_command,
                                       "events": events
                                       }

    return json.dumps(report_json, indent=4)


if __name__ == "__main__":
    generator = StreamReportGenerator("2025-11-24 12:18:00", 120, package="com.somewearlabs.atak.debug")
    reports = generator.collect_latency_reports()
    generator.generate_html()
    # generator.printReport()
