package com.foxx.androidcast.dev;

import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.foxx.androidcast.R;

import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

/** Runs async diagnostics and binds results to the Diagnostics tab cards. */
public final class DevDiagnosticsController {
    public enum Section {
        HW(R.id.dev_diag_hw, R.string.dev_diag_hw_title),
        CAMERA(R.id.dev_diag_camera, R.string.dev_diag_camera_title),
        AUDIO(R.id.dev_diag_audio, R.string.dev_diag_audio_title),
        AUDIO_CODECS(R.id.dev_diag_audio_codecs, R.string.dev_diag_audio_codecs_title),
        NETWORK(R.id.dev_diag_network, R.string.dev_diag_network_title),
        CASTING(R.id.dev_diag_casting, R.string.dev_diag_casting_title),
        ADVERTISEMENT(R.id.dev_diag_advertisement, R.string.dev_diag_advertisement_title);

        final int includeId;
        final int titleRes;

        Section(int includeId, int titleRes) {
            this.includeId = includeId;
            this.titleRes = titleRes;
        }
    }

    private final AppCompatActivity activity;
    private final View diagnosticsRoot;
    private final Map<Section, CardViews> cards = new LinkedHashMap<>();
    private final ExecutorService executor = Executors.newSingleThreadExecutor();
    private final Handler main = new Handler(Looper.getMainLooper());
    private final AtomicBoolean cancel = new AtomicBoolean(false);
    private final AtomicBoolean running = new AtomicBoolean(false);

    private DevDiagnosticsReport lastReport;

    public DevDiagnosticsController(AppCompatActivity activity) {
        this.activity = activity;
        this.diagnosticsRoot = activity.findViewById(R.id.dev_panel_diagnostics);
        for (Section s : Section.values()) {
            View cardRoot = diagnosticsRoot.findViewById(s.includeId);
            TextView title = cardRoot.findViewById(R.id.dev_diag_card_title);
            TextView body = cardRoot.findViewById(R.id.dev_diag_card_body);
            ProgressBar spinner = cardRoot.findViewById(R.id.dev_diag_card_spinner);
            title.setText(activity.getString(s.titleRes));
            body.setText(R.string.dev_diag_pending);
            cards.put(s, new CardViews(activity, body, spinner));
        }
        Button stop = diagnosticsRoot.findViewById(R.id.btn_dev_diag_stop);
        stop.setOnClickListener(v -> stopTests());
    }

    public void onDiagnosticsTabShown() {
        if (!running.get()) {
            startTests();
        }
    }

    public void onDestroy() {
        cancel.set(true);
        executor.shutdownNow();
    }

    public void stopTests() {
        cancel.set(true);
    }

    /** Last completed report (for future BE upload). */
    public DevDiagnosticsReport getLastReport() {
        return lastReport;
    }

    private void startTests() {
        cancel.set(false);
        running.set(true);
        for (CardViews c : cards.values()) {
            c.showRunning();
        }
        executor.execute(this::runSuite);
    }

    private void runSuite() {
        Map<String, String> sectionText = new LinkedHashMap<>();
        try {
            runSection(Section.HW, () ->
                    DevDiagnosticsProbe.probeHardwareAcceleration(activity), sectionText);
            runSection(Section.CAMERA, () ->
                    DevDiagnosticsProbe.probeCameraCapture(activity), sectionText);
            runSection(Section.AUDIO, () ->
                    DevDiagnosticsProbe.probeAudioCapture(activity), sectionText);
            runSection(Section.AUDIO_CODECS, () ->
                    DevDiagnosticsProbe.probeAudioCodecs(activity), sectionText);
            runSection(Section.NETWORK, () ->
                    DevDiagnosticsProbe.probeNetwork(activity, cancel), sectionText);
            runSection(Section.CASTING, () ->
                    DevDiagnosticsProbe.probeCastingExtras(activity), sectionText);
            runSection(Section.ADVERTISEMENT, () ->
                    DevDiagnosticsProbe.probeFullAdvertisementInfo(activity), sectionText);
        } finally {
            if (!cancel.get()) {
                lastReport = new DevDiagnosticsReport(System.currentTimeMillis(), sectionText);
            }
            main.post(() -> {
                running.set(false);
                if (cancel.get()) {
                    for (CardViews c : cards.values()) {
                        c.showStopped();
                    }
                }
            });
        }
    }

    private void runSection(Section section, Probe probe, Map<String, String> out) {
        if (cancel.get()) {
            main.post(() -> cards.get(section).showStopped());
            return;
        }
        main.post(() -> cards.get(section).showRunning());
        String text;
        try {
            text = probe.run();
        } catch (Exception e) {
            text = "Error: " + e.getMessage();
        }
        if (cancel.get()) {
            main.post(() -> cards.get(section).showStopped());
            return;
        }
        out.put(section.name().toLowerCase(Locale.ROOT), text);
        String finalText = text;
        main.post(() -> cards.get(section).showResult(finalText));
    }

    private interface Probe {
        String run();
    }

    private static final class CardViews {
        private final AppCompatActivity activity;
        private final TextView body;
        private final ProgressBar spinner;

        CardViews(AppCompatActivity activity, TextView body, ProgressBar spinner) {
            this.activity = activity;
            this.body = body;
            this.spinner = spinner;
        }

        void showRunning() {
            spinner.setVisibility(View.VISIBLE);
            body.setText(activity.getString(R.string.dev_diag_running));
        }

        void showResult(String text) {
            spinner.setVisibility(View.GONE);
            body.setText(text);
        }

        void showStopped() {
            spinner.setVisibility(View.GONE);
            body.setText(activity.getString(R.string.dev_diag_stopped));
        }
    }
}
