package com.dragonssh.xhttpdemo;

import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.recyclerview.widget.RecyclerView;

import com.dragonssh.xhttpdemo.core.logger.LogItem;
import com.dragonssh.xhttpdemo.core.logger.SkStatus;
import com.google.android.material.card.MaterialCardView;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

final class LogAdapter extends RecyclerView.Adapter<LogAdapter.LogViewHolder> {
    private static final int MAX_STORED_ENTRIES = 500;
    private static final int MAX_VISIBLE_ENTRIES = 250;

    private final Context context;
    private final List<Entry> allEntries = new ArrayList<>();
    private final List<Entry> visibleEntries = new ArrayList<>();
    private final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS", Locale.US);
    private boolean showDebug;

    LogAdapter(Context context) {
        this.context = context.getApplicationContext();
    }

    void replace(LogItem[] items) {
        allEntries.clear();
        if (items != null) {
            int start = Math.max(0, items.length - MAX_STORED_ENTRIES);
            for (int i = start; i < items.length; i++) {
                Entry entry = toEntry(items[i]);
                if (entry != null) allEntries.add(entry);
            }
        }
        rebuildVisible();
    }

    void add(LogItem item) {
        Entry entry = toEntry(item);
        if (entry == null) return;

        allEntries.add(entry);
        while (allEntries.size() > MAX_STORED_ENTRIES) allEntries.remove(0);

        if (isVisible(entry)) {
            visibleEntries.add(entry);
            notifyItemInserted(visibleEntries.size() - 1);
            if (visibleEntries.size() > MAX_VISIBLE_ENTRIES) {
                visibleEntries.remove(0);
                notifyItemRemoved(0);
            }
        }
    }

    void setShowDebug(boolean showDebug) {
        if (this.showDebug == showDebug) return;
        this.showDebug = showDebug;
        rebuildVisible();
    }

    void clear() {
        int count = visibleEntries.size();
        allEntries.clear();
        visibleEntries.clear();
        if (count > 0) notifyItemRangeRemoved(0, count);
    }

    boolean isEmpty() {
        return visibleEntries.isEmpty();
    }

    String asPlainText() {
        StringBuilder out = new StringBuilder();
        for (Entry entry : visibleEntries) {
            if (out.length() > 0) out.append('\n');
            out.append(entry.time)
                    .append(" [")
                    .append(entry.level.name())
                    .append("] ")
                    .append(entry.message);
        }
        return out.toString();
    }

    @NonNull
    @Override
    public LogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_log, parent, false);
        return new LogViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull LogViewHolder holder, int position) {
        Entry entry = visibleEntries.get(position);
        int accent = colorFor(entry.level);

        holder.time.setText(entry.time);
        holder.level.setText(entry.level.name());
        holder.message.setText(entry.message);
        holder.card.setStrokeColor(accent);

        Drawable background = holder.level.getBackground();
        if (background != null) {
            Drawable wrapped = DrawableCompat.wrap(background.mutate());
            DrawableCompat.setTintList(wrapped, ColorStateList.valueOf(accent));
            holder.level.setBackground(wrapped);
        }
    }

    @Override
    public int getItemCount() {
        return visibleEntries.size();
    }

    private void rebuildVisible() {
        visibleEntries.clear();
        for (Entry entry : allEntries) {
            if (isVisible(entry)) visibleEntries.add(entry);
        }
        while (visibleEntries.size() > MAX_VISIBLE_ENTRIES) visibleEntries.remove(0);
        notifyDataSetChanged();
    }

    private boolean isVisible(Entry entry) {
        return showDebug
                || (entry.level != SkStatus.LogLevel.DEBUG
                && entry.level != SkStatus.LogLevel.VERBOSE);
    }

    private Entry toEntry(LogItem item) {
        if (item == null) return null;
        String raw;
        try {
            raw = item.getString(context);
        } catch (Exception e) {
            raw = item.getMessage();
        }
        if (raw == null) return null;

        String clean = Html.fromHtml(raw, Html.FROM_HTML_MODE_LEGACY)
                .toString()
                .replace('\u00A0', ' ')
                .trim();
        if (clean.isEmpty()) return null;

        long timestamp = item.getLogtime();
        SkStatus.LogLevel level = item.getLogLevel() != null
                ? item.getLogLevel() : SkStatus.LogLevel.INFO;
        return new Entry(timeFormat.format(new Date(timestamp)), level, clean);
    }

    private int colorFor(SkStatus.LogLevel level) {
        int resource;
        switch (level) {
            case ERROR:
                resource = R.color.log_error;
                break;
            case WARNING:
                resource = R.color.log_warning;
                break;
            case DEBUG:
                resource = R.color.log_debug;
                break;
            case VERBOSE:
                resource = R.color.log_verbose;
                break;
            case INFO:
            default:
                resource = R.color.log_info;
                break;
        }
        return ContextCompat.getColor(context, resource);
    }

    static final class LogViewHolder extends RecyclerView.ViewHolder {
        final MaterialCardView card;
        final TextView time;
        final TextView level;
        final TextView message;

        LogViewHolder(@NonNull View itemView) {
            super(itemView);
            card = itemView.findViewById(R.id.log_card);
            time = itemView.findViewById(R.id.log_time);
            level = itemView.findViewById(R.id.log_level);
            message = itemView.findViewById(R.id.log_message);
        }
    }

    private static final class Entry {
        final String time;
        final SkStatus.LogLevel level;
        final String message;

        Entry(String time, SkStatus.LogLevel level, String message) {
            this.time = time;
            this.level = level;
            this.message = message;
        }
    }
}
