package com.auto.clicker.autoclicker.room.repository;

import android.content.Context;
import android.util.Log;

import androidx.room.Transaction;

import com.auto.clicker.autoclicker.data.Event;
import com.auto.clicker.autoclicker.data.EventWrapper;
import com.auto.clicker.autoclicker.data.PointEvent;
import com.auto.clicker.autoclicker.data.SlideEvent;
import com.auto.clicker.autoclicker.room.AppDatabase;
import com.auto.clicker.autoclicker.room.SolutionWithEventsAndPoints;
import com.auto.clicker.autoclicker.room.dao.EventEntityDao;
import com.auto.clicker.autoclicker.room.dao.SolutionDao;
import com.auto.clicker.autoclicker.room.dao.TouchPointDao;
import com.auto.clicker.autoclicker.room.entity.EventEntity;
import com.auto.clicker.autoclicker.room.entity.Solution;
import com.auto.clicker.autoclicker.room.entity.TouchPoint;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class EventRepository {
    private final SolutionDao solutionDao;
    private final TouchPointDao touchPointDao;
    private final EventEntityDao eventEntityDao;
    private final ExecutorService databaseWriteExecutor;

    public EventRepository(Context context) {
        AppDatabase db = AppDatabase.getInstance(context);
        this.solutionDao = db.solutionDao();
        this.touchPointDao = db.pointDao();
        this.eventEntityDao = db.eventEntityDao();
        this.databaseWriteExecutor = Executors.newFixedThreadPool(2);
    }

    public void insertSolutionWithEvents(Solution solution, List<EventWrapper> runtimeEvents, RepositoryCallback<Long> callback) {
        databaseWriteExecutor.execute(() -> {
            long solutionId = -1;
            try {
                solutionId = solutionDao.insertSolution(solution);
                if (solutionId == -1) {
                    throw new RuntimeException("Failed to insert solution.");
                }

                for (EventWrapper wrapper : runtimeEvents) {
                    Event event = wrapper.getEvent();
                    int order = wrapper.getOrder();

                    long eventEntityId;

                    if (wrapper.getType() == EventWrapper.EventType.POINT && event instanceof PointEvent) {
                        PointEvent pointEvent = (PointEvent) event;
                        TouchPoint touchPoint = new TouchPoint(pointEvent.getX(), pointEvent.getY());
                        long pointId = touchPointDao.insertPoint(touchPoint);
                        if (pointId == -1)
                            throw new RuntimeException("Failed to insert touch point for PointEvent.");

                        EventEntity eventEntity = new EventEntity(
                                solutionId, // 使用 long 类型的 solutionId
                                wrapper.getType().name(),
                                order,
                                pointId,
                                null,
                                null,
                                0
                        );
                        eventEntityId = eventEntityDao.insertEventEntity(eventEntity);
                        if (eventEntityId == -1)
                            throw new RuntimeException("Failed to insert EventEntity for PointEvent.");

                    } else if (wrapper.getType() == EventWrapper.EventType.SLIDE && event instanceof SlideEvent) {
                        SlideEvent slideEvent = (SlideEvent) event;
                        TouchPoint startTouchPoint = new TouchPoint(slideEvent.getStartPoint().getX(), slideEvent.getStartPoint().getY());
                        long startPointId = touchPointDao.insertPoint(startTouchPoint);
                        if (startPointId == -1)
                            throw new RuntimeException("Failed to insert start touch point for SlideEvent.");

                        TouchPoint endTouchPoint = new TouchPoint(slideEvent.getEndPoint().getX(), slideEvent.getEndPoint().getY());
                        long endPointId = touchPointDao.insertPoint(endTouchPoint);
                        if (endPointId == -1)
                            throw new RuntimeException("Failed to insert end touch point for SlideEvent.");

                        EventEntity eventEntity = new EventEntity(
                                solutionId, // 使用 long 类型的 solutionId
                                wrapper.getType().name(),
                                order,
                                null,
                                startPointId,
                                endPointId,
                                0 // duration for slide events, you might want to get this from SlideEvent
                        );
                        eventEntityId = eventEntityDao.insertEventEntity(eventEntity);
                        if (eventEntityId == -1)
                            throw new RuntimeException("Failed to insert EventEntity for SlideEvent.");
                    }
                }
                if (callback != null) {
                    callback.onComplete(solutionId);
                }
            } catch (Exception e) {
                Log.e("EventRepository", "Error inserting solution with events: " + e.getMessage(), e);
                if (callback != null) {
                    callback.onError(e);
                }
            }
        });
    }

    public void getAllSolutions(RepositoryCallback<List<Solution>> callback) {
        databaseWriteExecutor.execute(() -> {
            try {
                List<Solution> solutions = solutionDao.getAllSolutions();
                if (callback != null) callback.onComplete(solutions);
            } catch (Exception e) {
                Log.e("EventRepository", "Error getting all solutions: " + e.getMessage(), e);
                if (callback != null) callback.onError(e);
            }
        });
    }

    public void updateSolutionName(long solutionId, String newName, RepositoryCallback<Void> callback) {
        databaseWriteExecutor.execute(() -> {
            try {
                int updatedRows = solutionDao.updateSolutionName(solutionId, newName);
                if (updatedRows > 0) {
                    if (callback != null) callback.onComplete(null);
                } else {
                    throw new RuntimeException("Failed to update solution name, solution not found.");
                }
            } catch (Exception e) {
                Log.e("EventRepository", "Error updating solution name: " + e.getMessage(), e);
                if (callback != null) callback.onError(e);
            }
        });
    }

    @Transaction
    public void deleteSolution(long solutionId, RepositoryCallback<Void> callback) {
        databaseWriteExecutor.execute(() -> {
            try {
                solutionDao.deleteSolutionById(solutionId);
                if (callback != null) callback.onComplete(null);
            } catch (Exception e) {
                Log.e("EventRepository", "Error deleting solution: " + e.getMessage(), e);
                if (callback != null) callback.onError(e);
            }
        });
    }

    public void getSolutionWithEventsAndPoints(long solutionId, RepositoryCallback<SolutionWithEventsAndPoints> callback) {
        databaseWriteExecutor.execute(() -> {
            try {
                SolutionWithEventsAndPoints data = solutionDao.getSolutionWithEventsAndPoints(solutionId);
                if (callback != null) callback.onComplete(data);
            } catch (Exception e) {
                Log.e("EventRepository", "Error getting solution with events and points: " + e.getMessage(), e);
                if (callback != null) callback.onError(e);
            }
        });
    }

    @Transaction
    public void importSolutionFromData(SolutionWithEventsAndPoints importedData, RepositoryCallback<Void> callback) {
        databaseWriteExecutor.execute(() -> {
            try {
                Solution newSolution = new Solution(importedData.solution.solutionName, importedData.solution.mode);
                long newSolutionId = solutionDao.insertSolution(newSolution);

                if (newSolutionId == -1) {
                    throw new RuntimeException("Failed to import solution: Could not insert new Solution.");
                }

                for (SolutionWithEventsAndPoints.EventWithPoints eventWithPoints : importedData.events) {
                    Long newClickPointId = null;
                    if (eventWithPoints.clickTouchPoint != null) {
                        TouchPoint newClickPoint = new TouchPoint(eventWithPoints.clickTouchPoint.x, eventWithPoints.clickTouchPoint.y);
                        newClickPointId = touchPointDao.insertPoint(newClickPoint);
                        if (newClickPointId == -1) throw new RuntimeException("Failed to import click point.");
                    }

                    Long newSlideStartPointId = null;
                    if (eventWithPoints.slideStartTouchPoint != null) {
                        TouchPoint newSlideStartPoint = new TouchPoint(eventWithPoints.slideStartTouchPoint.x, eventWithPoints.slideStartTouchPoint.y);
                        newSlideStartPointId = touchPointDao.insertPoint(newSlideStartPoint);
                        if (newSlideStartPointId == -1) throw new RuntimeException("Failed to import slide start point.");
                    }

                    Long newSlideEndPointId = null;
                    if (eventWithPoints.slideEndTouchPoint != null) {
                        TouchPoint newSlideEndPoint = new TouchPoint(eventWithPoints.slideEndTouchPoint.x, eventWithPoints.slideEndTouchPoint.y);
                        newSlideEndPointId = touchPointDao.insertPoint(newSlideEndPoint);
                        if (newSlideEndPointId == -1) throw new RuntimeException("Failed to import slide end point.");
                    }

                    EventEntity newEventEntity = new EventEntity(
                            newSolutionId, // 关联到新生成的方案ID
                            eventWithPoints.eventEntity.eventType,
                            eventWithPoints.eventEntity.orderInSolution,
                            newClickPointId,
                            newSlideStartPointId,
                            newSlideEndPointId,
                            eventWithPoints.eventEntity.duration
                    );
                    long newEventEntityId = eventEntityDao.insertEventEntity(newEventEntity);
                    if (newEventEntityId == -1) {
                        throw new RuntimeException("Failed to import event entity.");
                    }
                }
                if (callback != null) callback.onComplete(null);
            } catch (Exception e) {
                Log.e("EventRepository", "Error importing solution: " + e.getMessage(), e);
                if (callback != null) {
                    callback.onError(e);
                }
            }
        });
    }

    public SolutionWithEventsAndPoints importSolutionFromJson(String jsonString) {
        try {
            Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
            return gson.fromJson(jsonString, SolutionWithEventsAndPoints.class);
        } catch (Exception e) {
            Log.e("EventRepository", "Error parsing JSON for import: " + e.getMessage(), e);
            return null;
        }
    }

    @Transaction
    public void saveDuplicateSolution(SolutionWithEventsAndPoints originalData, RepositoryCallback<Void> callback) {
        databaseWriteExecutor.execute(() -> {
            try {
                // 创建新的 Solution 实例
                Solution newSolution = new Solution(originalData.solution.solutionName + " (1)", originalData.solution.mode);
                long newSolutionId = solutionDao.insertSolution(newSolution);

                if (newSolutionId == -1) {
                    throw new RuntimeException("Failed to duplicate solution: Could not insert new Solution.");
                }

                for (SolutionWithEventsAndPoints.EventWithPoints eventWithPoints : originalData.events) {
                    Long newClickPointId = null;
                    if (eventWithPoints.clickTouchPoint != null) {
                        TouchPoint newClickPoint = new TouchPoint(eventWithPoints.clickTouchPoint.x, eventWithPoints.clickTouchPoint.y);
                        newClickPointId = touchPointDao.insertPoint(newClickPoint);
                        if (newClickPointId == -1)
                            throw new RuntimeException("Failed to duplicate click point.");
                    }

                    Long newSlideStartPointId = null;
                    if (eventWithPoints.slideStartTouchPoint != null) {
                        TouchPoint newSlideStartPoint = new TouchPoint(eventWithPoints.slideStartTouchPoint.x, eventWithPoints.slideStartTouchPoint.y);
                        newSlideStartPointId = touchPointDao.insertPoint(newSlideStartPoint);
                        if (newSlideStartPointId == -1)
                            throw new RuntimeException("Failed to duplicate slide start point.");
                    }

                    Long newSlideEndPointId = null;
                    if (eventWithPoints.slideEndTouchPoint != null) {
                        TouchPoint newSlideEndPoint = new TouchPoint(eventWithPoints.slideEndTouchPoint.x, eventWithPoints.slideEndTouchPoint.y);
                        newSlideEndPointId = touchPointDao.insertPoint(newSlideEndPoint);
                        if (newSlideEndPointId == -1)
                            throw new RuntimeException("Failed to duplicate slide end point.");
                    }

                    // 创建新的 EventEntity 实例
                    EventEntity newEventEntity = new EventEntity(
                            newSolutionId,
                            eventWithPoints.eventEntity.eventType,
                            eventWithPoints.eventEntity.orderInSolution,
                            newClickPointId,
                            newSlideStartPointId,
                            newSlideEndPointId,
                            eventWithPoints.eventEntity.duration
                    );
                    long newEventEntityId = eventEntityDao.insertEventEntity(newEventEntity);
                    if (newEventEntityId == -1) {
                        throw new RuntimeException("Failed to duplicate event entity.");
                    }
                }
                if (callback != null) callback.onComplete(null);
            } catch (Exception e) {
                Log.e("EventRepository", "Error duplicating solution: " + e.getMessage(), e);
                if (callback != null) callback.onError(e);
            }
        });
    }

    public String exportSolutionToJson(SolutionWithEventsAndPoints data) {
        Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().setPrettyPrinting().create();
        return gson.toJson(data);
    }

    public interface RepositoryCallback<T> {
        void onComplete(T result);

        void onError(Exception e);
    }
}