package com.lemenzo.gallery.video;
import com.lemenzo.gallery.ui.SystemInsets;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.graphics.Bitmap;
import android.os.Environment;
import android.provider.MediaStore;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import android.os.Bundle;
import android.widget.Switch;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;

import com.lemenzo.gallery.R;

import java.util.concurrent.ExecutorService;
import com.lemenzo.gallery.util.BackgroundExecutors;

public final class VideoEditorActivity extends Activity {
    private static final String EXTRA_URI = "uri";
    private final ExecutorService executor = BackgroundExecutors.single("LemenzoGalleryVideoEditor");
    private Uri source;
    private long durationMs;
    private SeekBar start, end;
    private TextView range;
    private Switch mute;
    private VideoView video;
    private int rotationDelta;
    private TextView rotationStatus;

    public static Intent intent(Context context, Uri uri) {
        return new Intent(context, VideoEditorActivity.class).setData(uri).putExtra(EXTRA_URI, uri.toString()).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }

    @Override public void onCreate(Bundle state) {
        super.onCreate(state); setContentView(R.layout.activity_video_editor);
        SystemInsets.apply(this);
        source = getIntent().getData();
        if (source == null) { String extra = getIntent().getStringExtra(EXTRA_URI); if (extra != null) source = Uri.parse(extra); }
        if (source == null) { Toast.makeText(this, R.string.video_save_failed, Toast.LENGTH_LONG).show(); finish(); return; }
        video = findViewById(R.id.video_editor_preview); video.setVideoURI(source); video.setOnPreparedListener(mp -> { mp.setLooping(true); video.start(); updatePlayPauseLabel(); });
        start = findViewById(R.id.video_start_seek); end = findViewById(R.id.video_end_seek); range = findViewById(R.id.video_range); mute = findViewById(R.id.video_mute); rotationStatus = findViewById(R.id.video_rotation_status);
        findViewById(R.id.video_close).setOnClickListener(v -> finish());
        findViewById(R.id.video_play_pause).setOnClickListener(v -> togglePlayback());
        findViewById(R.id.video_save).setOnClickListener(v -> save());
        findViewById(R.id.video_save_frame).setOnClickListener(v -> saveFrame());
        findViewById(R.id.video_rotate_left).setOnClickListener(v -> { rotationDelta = (rotationDelta + 270) % 360; updateRotation(); });
        findViewById(R.id.video_rotate_right).setOnClickListener(v -> { rotationDelta = (rotationDelta + 90) % 360; updateRotation(); });
        updateRotation();
        SeekBar.OnSeekBarChangeListener listener = new SeekBar.OnSeekBarChangeListener() {
            @Override public void onProgressChanged(SeekBar bar, int p, boolean fromUser) { if (fromUser) { normalizeRange(bar); if(durationMs>0&&video!=null)video.seekTo((int)Math.min(Integer.MAX_VALUE,positionMs(bar))); } updateRange(); }
            @Override public void onStartTrackingTouch(SeekBar b) {}
            @Override public void onStopTrackingTouch(SeekBar b) {
                if (b != start || durationMs <= 0) return;
                final long requested = positionMs(start);
                executor.execute(() -> {
                    long safe = requested;
                    try { safe = VideoTrimExporter.previousSyncMs(VideoEditorActivity.this, source, requested); }
                    catch (Exception ignored) {}
                    final long resolved = safe;
                    runOnUiThread(() -> {
                        if (isFinishing() || isDestroyed() || durationMs <= 0) return;
                        int progress = (int)Math.max(0, Math.min(1000, resolved * 1000L / durationMs));
                        start.setProgress(progress);
                        normalizeRange(start);
                        updateRange();
                    });
                });
            }
        };
        start.setOnSeekBarChangeListener(listener); end.setOnSeekBarChangeListener(listener); end.setProgress(1000);
        readDuration();
    }

    private void readDuration() {
        executor.execute(() -> {
            long resolved=0;
            MediaMetadataRetriever r=new MediaMetadataRetriever();
            try{r.setDataSource(this,source);String value=r.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);resolved=value==null?0:Long.parseLong(value);}
            catch(Exception ignored){resolved=0;}
            finally{try{r.release();}catch(IOException ignored){}}
            final long result=resolved;
            runOnUiThread(() -> {if(isFinishing()||isDestroyed())return;durationMs=result;updateRange();});
        });
    }

    private void togglePlayback(){
        if(video==null)return;
        if(video.isPlaying())video.pause();else video.start();
        updatePlayPauseLabel();
    }
    private void updatePlayPauseLabel(){
        TextView button=findViewById(R.id.video_play_pause);
        if(button!=null)button.setText(video!=null&&video.isPlaying()?R.string.pause:R.string.play);
    }

    private void normalizeRange(SeekBar changed) {
        if (start.getProgress() >= end.getProgress()) {
            if (changed == start) end.setProgress(Math.min(1000, start.getProgress() + 1));
            else start.setProgress(Math.max(0, end.getProgress() - 1));
        }
    }

    private long positionMs(SeekBar b) { return durationMs * b.getProgress() / 1000L; }
    private String time(long ms) { long total = ms / 1000; return String.format(java.util.Locale.US, "%d:%02d", total / 60, total % 60); }
    private void updateRange() { range.setText(time(positionMs(start)) + " – " + time(positionMs(end))); }
    private void updateRotation() { if (rotationStatus != null) rotationStatus.setText(getString(R.string.video_rotation, rotationDelta)); }

    private void saveFrame() {
        final long atMs = positionMs(start);
        findViewById(R.id.video_save_frame).setEnabled(false);
        executor.execute(() -> {
            Uri saved = null; Bitmap frame = null;
            try {
                MediaMetadataRetriever retriever = new MediaMetadataRetriever();
                try {
                    retriever.setDataSource(this, source);
                    frame = retriever.getFrameAtTime(atMs * 1000L, MediaMetadataRetriever.OPTION_CLOSEST);
                } finally { retriever.release(); }
                if (frame == null) throw new java.io.IOException("No video frame available");
                ContentValues values = new ContentValues();
                String stamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
                values.put(MediaStore.Images.Media.DISPLAY_NAME, "Lemenzo_frame_" + stamp + ".jpg");
                values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
                values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/Lemenzo");
                values.put(MediaStore.Images.Media.IS_PENDING, 1);
                saved = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
                if (saved == null) throw new java.io.IOException("MediaStore insert failed");
                try (OutputStream out = getContentResolver().openOutputStream(saved, "w")) {
                    if (out == null || !frame.compress(Bitmap.CompressFormat.JPEG, 95, out)) throw new java.io.IOException("Frame encode failed");
                }
                ContentValues publish = new ContentValues(); publish.put(MediaStore.Images.Media.IS_PENDING, 0);
                if (getContentResolver().update(saved, publish, null, null) != 1) throw new java.io.IOException("Frame publish failed");
            } catch (Exception e) {
                if (saved != null) getContentResolver().delete(saved, null, null); saved = null;
            } finally { if (frame != null && !frame.isRecycled()) frame.recycle(); }
            Uri result = saved;
            runOnUiThread(() -> {
                if (isFinishing() || isDestroyed()) return;
                findViewById(R.id.video_save_frame).setEnabled(true);
                Toast.makeText(this, result == null ? R.string.frame_save_failed : R.string.frame_saved, result == null ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT).show();
            });
        });
    }

    private void save() {
        final long s = positionMs(start), e = positionMs(end); if (e <= s) return;
        final boolean muteAudio = mute.isChecked();
        final int rotation = rotationDelta;
        setControlsEnabled(false); range.setText(R.string.video_saving);
        executor.execute(() -> {
            Uri out = null;
            try { out = VideoTrimExporter.export(this, source, s, e, muteAudio, rotation); }
            catch (Exception ignored) {}
            Uri result = out; runOnUiThread(() -> {
                if (isFinishing() || isDestroyed()) return; setControlsEnabled(true);
                if (result == null) { updateRange(); Toast.makeText(this, R.string.video_save_failed, Toast.LENGTH_LONG).show(); }
                else { Toast.makeText(this, R.string.video_saved, Toast.LENGTH_SHORT).show(); setResult(RESULT_OK, new Intent().setData(result)); }
            });
        });
    }

    private void setControlsEnabled(boolean enabled) {
        start.setEnabled(enabled); end.setEnabled(enabled); mute.setEnabled(enabled);
        findViewById(R.id.video_rotate_left).setEnabled(enabled);
        findViewById(R.id.video_rotate_right).setEnabled(enabled);
        findViewById(R.id.video_save_frame).setEnabled(enabled);
        findViewById(R.id.video_play_pause).setEnabled(enabled);
        findViewById(R.id.video_save).setEnabled(enabled);
    }

    @Override protected void onPause(){if(video!=null&&video.isPlaying())video.pause();updatePlayPauseLabel();super.onPause();}

    @Override protected void onDestroy() { executor.shutdownNow(); if(video!=null)video.stopPlayback(); super.onDestroy(); }
}
