package com.example.myapplication.ui;

import com.example.myapplication.R;
import com.example.myapplication.Utils;
import com.example.myapplication.service.UploadService;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.net.Uri;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.ImageView;
import android.content.ContentResolver;
import android.database.Cursor;
import android.provider.OpenableColumns;

import androidx.appcompat.app.AppCompatActivity;

import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import java.util.List;
import java.util.ArrayList;

// 添加自动分类功能所需的 import
import android.util.Log;
import android.content.Context;
import android.content.res.AssetManager;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

// Google ML Kit文字识别导入
import com.google.mlkit.vision.common.InputImage;

// HanLP开源代码导入
// 删除以下 UI 中不应存在的导入：
// import com.hankcs.hanlp.HanLP;
// import com.hankcs.hanlp.seg.common.Term;
// import com.hankcs.hanlp.dictionary.stopword.CoreStopWordDictionary;

// 文档处理相关导入
import com.tom_roush.pdfbox.android.PDFBoxResourceLoader;
import com.tom_roush.pdfbox.pdmodel.PDDocument;
import com.tom_roush.pdfbox.text.PDFTextStripper;

import com.example.myapplication.service.AuthService;
import com.example.myapplication.repository.NoteRepository;
import java.io.File;
import java.io.OutputStream;
import java.io.FileOutputStream;

public class UploadUI extends AppCompatActivity {
    private ProgressBar progressBar;
    private TextView progressText;
    private Button submitBtn;
    private ImageView previewImage;
    // 删除：private TextView ocrResultText;
    private TextView fileInfoText;
    private TextView categorySuggestions;
    private LinearLayout fileListContainer;

    private static final int REQ_OPEN_DOCUMENT = 1002;

    private final List<Uri> selectedFiles = new ArrayList<>();
    private Uri currentPreviewUri;
    private TextView classificationResultText;
    private com.example.myapplication.service.ClassificationService classificationService;
    
    // 聚合所有文件的文本内容
    private final StringBuilder aggregatedText = new StringBuilder();
    private final Set<String> processedFiles = new HashSet<>();
    private Long autoChapterId = null;
    private Long autoSubjectId = null;
    private java.util.List<com.example.myapplication.domain.Course> availableSubjects;
    private java.util.List<com.example.myapplication.domain.Chapter> availableChapters;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_upload);

        EditText titleEt = findViewById(R.id.noteTitle);
        Spinner subjectSp = findViewById(R.id.subjectSpinner);
        EditText contentEt = findViewById(R.id.contentInput); // 替换描述为内容
        EditText tagsEt = findViewById(R.id.tagsInput);
        progressBar = findViewById(R.id.progressBar);
        progressText = findViewById(R.id.progressText);
        submitBtn = findViewById(R.id.submitBtn);

        previewImage = findViewById(R.id.previewImage);
        // 删除：ocrResultText = findViewById(R.id.ocrResultText);
        fileInfoText = findViewById(R.id.fileInfoText);
        fileListContainer = findViewById(R.id.fileListContainer);
        
        // 初始化自动分类功能
        classificationResultText = findViewById(R.id.classificationResultText);
        com.tom_roush.pdfbox.android.PDFBoxResourceLoader.init(getApplicationContext());
        try {
            classificationService = new com.example.myapplication.service.ClassificationService(this);
        } catch (Exception e) {
            android.util.Log.e("UploadUI", "ClassificationService init failed", e);
        }

        // 初始化科目下拉（数据库）
        try {
            com.example.myapplication.repository.CourseRepository cr = new com.example.myapplication.repository.CourseRepository(this);
            String account = com.example.myapplication.service.AuthService.getCurrentUser(this);
            com.example.myapplication.repository.UserRepository ur = new com.example.myapplication.repository.UserRepository(this);
            com.example.myapplication.domain.User u = ur.getUserByAccount(account);
            Long majorId = u != null ? u.getMajorId() : null;
            String gradeText = ur.getGradeText(account);
            availableSubjects = cr.getCoursesByMajorAndGrade(majorId, gradeText);
            if (availableSubjects == null || availableSubjects.isEmpty()) availableSubjects = cr.getCoursesByMajor(majorId);
            if (availableSubjects == null || availableSubjects.isEmpty()) availableSubjects = cr.getAllCourses();
            java.util.List<String> subjectNames = new java.util.ArrayList<>();
            for (com.example.myapplication.domain.Course c : availableSubjects) subjectNames.add(c.getName());
            android.widget.ArrayAdapter<String> subjAdapter = new android.widget.ArrayAdapter<>(
                    this, android.R.layout.simple_spinner_item, subjectNames);
            subjAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            subjectSp.setAdapter(subjAdapter);
            subjectSp.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() {
                @Override public void onItemSelected(android.widget.AdapterView<?> parent, android.view.View view, int position, long id) {
                    if (position >= 0 && position < availableSubjects.size()) {
                        long sid = availableSubjects.get(position).getId();
                        loadChaptersForSubject(sid);
                    }
                }
                @Override public void onNothingSelected(android.widget.AdapterView<?> parent) {}
            });
        } catch (Exception e) {
            android.util.Log.e("UploadUI", "init subjects failed", e);
        }

        findViewById(R.id.selectFileBtn).setOnClickListener(v -> {
            Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            intent.setType("*/*");
            intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {
                    "image/*",
                    "application/pdf",
                    "application/msword",
                    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                    "text/plain"
            });
            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
            startActivityForResult(intent, REQ_OPEN_DOCUMENT);
        });

        findViewById(R.id.backBtn).setOnClickListener(v -> finish());

        submitBtn.setOnClickListener(v -> {
            String title = titleEt.getText().toString().trim();
            String content = contentEt.getText().toString().trim();
            if (title.isEmpty()) {
                com.example.myapplication.Utils.toast(this, "请输入笔记标题");
                return;
            }
            simulateUpload(title, content);
        });
    }

    // 将 ProgressListener 的实现改回本地 Handler 进度循环，避免生成 UploadUI$1.class
    private void simulateUpload(String title, String content) {
        submitBtn.setEnabled(false);
        progressBar.setProgress(0);
        progressText.setText("上传中... 0%");
        progressBar.setVisibility(android.view.View.VISIBLE);
        progressText.setVisibility(android.view.View.VISIBLE);

        android.os.Handler handler = new android.os.Handler();
        for (int i = 1; i <= 10; i++) {
            final int progress = i * 10;
            handler.postDelayed(() -> {
                progressBar.setProgress(progress);
                progressText.setText("上传中... " + progress + "%");
                if (progress == 100) {
                    persistNoteAsync(title, content);
                }
            }, i * 200);
        }
    }

    // 新增：持久化到 SQLite，并复制首个选择的文件到内部存储
    // 确保这是类级方法，而不是嵌套在 onCreate 里
    private void persistNoteAsync(String title, String content) {
        new Thread(() -> {
            boolean ok = false;
            String errMsg = null;
            try {
                String uploaderId = com.example.myapplication.service.AuthService.getCurrentUser(this);
                
                Long courseId = autoChapterId;
                try {
                    android.widget.Spinner chapterSp = findViewById(R.id.chapterSpinner);
                    if (chapterSp != null && availableChapters != null && !availableChapters.isEmpty()) {
                        int pos = chapterSp.getSelectedItemPosition();
                        if (pos >= 0 && pos < availableChapters.size()) courseId = availableChapters.get(pos).getId();
                    }
                } catch (Exception ignored) {}
                
                String storedPath = null;
                if (!selectedFiles.isEmpty()) {
                    android.net.Uri uri = selectedFiles.get(0);
                    String name = com.example.myapplication.service.UploadService.getDisplayName(this, uri);
                    java.io.File dir = new java.io.File(getFilesDir(), "notes");
                    if (!dir.exists()) dir.mkdirs();
                    java.io.File out = new java.io.File(
                            dir,
                            System.currentTimeMillis() + "_" + (name == null ? "note" : name)
                    );
                    copyUriToFile(uri, out);
                    storedPath = out.getAbsolutePath();
                }

                com.example.myapplication.repository.NoteRepository repo =
                        new com.example.myapplication.repository.NoteRepository(this);
                ok = repo.saveNote(title, content, storedPath, courseId, uploaderId);
                if (ok && uploaderId != null && !uploaderId.isEmpty()) {
                    new com.example.myapplication.repository.UserRepository(this)
                            .addPoints(uploaderId, 10);
                }
            } catch (Exception e) {
                errMsg = e.getMessage();
                android.util.Log.e("UploadUI", "persistNote failed", e);
            }

            boolean finalOk = ok;
            String finalErr = errMsg;
            runOnUiThread(() -> {
                submitBtn.setEnabled(true);
                if (finalOk) {
                    progressText.setText("上传成功 100%");
                    com.example.myapplication.Utils.toast(this, "上传成功！");
                    finish();
                } else {
                    progressText.setText("上传失败");
                    com.example.myapplication.Utils.toast(this, "上传失败" + (finalErr != null ? "：" + finalErr : ""));
                }
            });
        }).start();
    }

    // 新增：复制文件（使用完整类名，避免 import 依赖）
    private void copyUriToFile(android.net.Uri uri, java.io.File outFile) throws java.io.IOException {
        try (java.io.InputStream in = getContentResolver().openInputStream(uri);
             java.io.OutputStream out = new java.io.FileOutputStream(outFile)) {
            byte[] buf = new byte[8192];
            int len;
            while ((len = in.read(buf)) != -1) {
                out.write(buf, 0, len);
            }
            out.flush();
        }
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != RESULT_OK || data == null) return;

        // 已删除：REQ_PICK_IMAGE 分支

        if (requestCode == REQ_OPEN_DOCUMENT) {
            if (data.getClipData() != null) {
                for (int i = 0; i < data.getClipData().getItemCount(); i++) {
                    Uri uri = data.getClipData().getItemAt(i).getUri();
                    addFileItem(uri);
                }
            } else {
                Uri uri = data.getData();
                if (uri != null) addFileItem(uri);
            }
            updatePreviewFromFirstImage();
        }
    }

    private void autoFillFields(String fullText, String displayName, String mime) {
        EditText titleEt = findViewById(R.id.noteTitle);
        Spinner subjectSp = findViewById(R.id.subjectSpinner);
        EditText contentEt = findViewById(R.id.contentInput);
        EditText tagsEt = findViewById(R.id.tagsInput);

        String title = titleEt.getText().toString().trim();
        String fromName = (displayName == null) ? "" : displayName.replaceAll("\\.[^.]+$", "");

        if (title.isEmpty()) {
            if (fullText != null && !fullText.trim().isEmpty()) {
                for (String line : fullText.split("\\r?\\n")) {
                    String t = line.trim();
                    if (!t.isEmpty()) { title = t; break; }
                }
            }
            if (title.isEmpty() && !fromName.isEmpty()) title = fromName;
            if (!title.isEmpty()) titleEt.setText(title);
        }

        String base = (fullText != null && !fullText.isEmpty()) ? fullText : fromName;
        String subjectGuess = guessSubjectFromDatabase(base);
        setSpinnerSelectionByText(subjectSp, subjectGuess);

        // 仅当内容输入框为空时，才用摘要填充，避免覆盖完整 OCR 文本
        boolean isContentEmpty = contentEt.getText().toString().trim().isEmpty();

        if (fullText != null && !fullText.trim().isEmpty()) {
            String[] lines = fullText.split("\\r?\\n");
            StringBuilder sb = new StringBuilder();
            int count = 0;
            for (String l : lines) {
                String line = l.trim();
                if (line.isEmpty() || line.startsWith("---") || line.endsWith("---")) continue;
                sb.append(line).append(" ");
                if (++count >= 5) break;
            }
            String desc = sb.toString().trim();
            if (!desc.isEmpty() && isContentEmpty) {
                if (processedFiles.size() > 1) {
                    desc = "基于" + processedFiles.size() + "个文件的内容：" + desc;
                }
                contentEt.setText(desc);
            }
        } else if (!fromName.isEmpty() && isContentEmpty) {
            contentEt.setText("文档：" + fromName + (mime != null ? "（" + mime + "）" : ""));
        }

        StringBuilder tags = new StringBuilder();
        
        // 首先添加学科名称作为标签
        if (subjectGuess != null) {
            tags.append(subjectGuess);
        }
        
        // 然后添加具体的技术标签
        for (String k : new String[]{"DH参数","矩阵变换","坐标系","机械臂","运动学","动力学","雅可比矩阵","线性代数","函数","极限","力学","算法","数据结构","人工智能","机器学习","深度学习","神经网络","计算机视觉","自然语言处理","数据库","操作系统","计算机网络","软件工程","编程语言","Web开发","移动开发"}) {
            if ((base + " " + title).toLowerCase().contains(k.toLowerCase())) {
                if (tags.length() > 0) tags.append(",");
                tags.append(k);
            }
        }
        
        if (tags.length() > 0) {
            String currentTags = tagsEt.getText().toString().trim();
            if (currentTags.isEmpty()) {
                tagsEt.setText(tags.toString());
            } else {
                // 合并标签，避免重复
                Set<String> tagSet = new HashSet<>(Arrays.asList(currentTags.split(",")));
                for (String tag : tags.toString().split(",")) {
                    tagSet.add(tag.trim());
                }
                StringBuilder sb = new StringBuilder();
                for (String s : tagSet) {
                    if (s == null) continue;
                    String trimmed = s.trim();
                    if (trimmed.isEmpty()) continue;
                    if (sb.length() > 0) sb.append(",");
                    sb.append(trimmed);
                }
                tagsEt.setText(sb.toString());
            }
        }

        // （已删除）智能分类建议的拼接与设置，避免对不存在视图的 setText 调用
        // StringBuilder sug = new StringBuilder();
        // sug.append("智能分类建议：\n");
        // if (subjectGuess != null) sug.append("科目：").append(subjectGuess).append("\n");
        // if (tags.length() > 0) sug.append("标签：").append(tags);
        // categorySuggestions.setText(sug.toString());
        
        // 触发自动分类
        onOcrTextReady(fullText);
    }

    private void setSpinnerSelectionByText(Spinner sp, String text) {
        if (text == null) return;
        android.widget.ArrayAdapter adapter = (android.widget.ArrayAdapter) sp.getAdapter();
        if (adapter == null) return;
        for (int i = 0; i < adapter.getCount(); i++) {
            Object item = adapter.getItem(i);
            if (item != null && text.equals(item.toString())) {
                sp.setSelection(i);
                return;
            }
        }
    }

    private String guessSubjectFromDatabase(String base) {
        if (base == null) return null;
        String lower = base.toLowerCase();
        try {
            com.example.myapplication.repository.CourseRepository cr = new com.example.myapplication.repository.CourseRepository(this);
            java.util.List<com.example.myapplication.domain.Course> subjects = cr.getAllCourses();
            for (com.example.myapplication.domain.Course s : subjects) {
                String name = s.getName() == null ? "" : s.getName();
                String code = s.getCode() == null ? "" : s.getCode();
                if (!name.isEmpty() && lower.contains(name.toLowerCase())) return name;
                if (!code.isEmpty() && lower.contains(code.toLowerCase())) return name;
                java.util.List<com.example.myapplication.domain.Chapter> chapters = cr.getChaptersBySubjectId(s.getId());
                for (com.example.myapplication.domain.Chapter ch : chapters) {
                    String cn = ch.getName() == null ? "" : ch.getName();
                    if (!cn.isEmpty() && lower.contains(cn.toLowerCase())) return name;
                }
            }
        } catch (Exception e) {
            android.util.Log.e("UploadUI", "guessSubjectFromDatabase failed", e);
        }
        return null;
    }

    private void addFileItem(android.net.Uri uri) {
        if (uri == null) return;
        selectedFiles.add(uri);

        String name = UploadService.getDisplayName(this, uri);
        String mime = UploadService.getMimeType(this, uri);

        LinearLayout row = new LinearLayout(this);
        row.setOrientation(LinearLayout.HORIZONTAL);
        row.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

        TextView tv = new TextView(this);
        tv.setText((name == null ? "文件" : name) + "  |  " + (mime == null ? "" : mime));
        tv.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1));

        Button removeBtn = new Button(this);
        removeBtn.setText("移除");
        removeBtn.setOnClickListener(v -> {
            selectedFiles.remove(uri);
            fileListContainer.removeView(row);
            
            // 清理聚合文本内容
            String fileKey = name + "_" + mime;
            if (processedFiles.contains(fileKey)) {
                processedFiles.remove(fileKey);
                // 重新构建聚合文本
                rebuildAggregatedText();
            }
            
            if (currentPreviewUri != null && currentPreviewUri.equals(uri)) {
                if (previewImage != null) previewImage.setImageDrawable(null);
                // 删除旧：ocrResultText.setText(...)
                if (fileInfoText != null) fileInfoText.setText("未选择文件");
                updatePreviewFromFirstImage();
            }
        });

        row.addView(tv);
        row.addView(removeBtn);
        fileListContainer.addView(row);
        
        // 自动提取文档文本内容
        extractDocumentText(uri, name, mime);
    }
    
    // 文档文本提取方法
    // 将图片 OCR 调用切换到 OCRService（替换原 runTextRecognition 的使用）
    private void extractDocumentText(android.net.Uri uri, String displayName, String mimeType) {
        new Thread(() -> {
            try {
                String extractedText = "";
                if (mimeType != null) {
                    if (mimeType.contains("pdf")) {
                        extractedText = com.example.myapplication.service.UploadService.extractPdfText(this, uri);
                    } else if (mimeType.contains("text/plain")) {
                        extractedText = com.example.myapplication.service.UploadService.extractPlainText(this, uri);
                    } else if (mimeType.contains("msword") || mimeType.contains("wordprocessingml")) {
                        runOnUiThread(() -> {
                            if (fileInfoText != null) {
                                fileInfoText.setText("Word文档暂不支持，建议转换为PDF或文本格式");
                            }
                        });
                        return;
                    } else if (mimeType.contains("image")) {
                        runOnUiThread(() -> {
                            try {
                                com.google.mlkit.vision.common.InputImage image =
                                        com.google.mlkit.vision.common.InputImage.fromFilePath(this, uri);
                                com.example.myapplication.service.OCRService.recognize(
                                        this,
                                        image,
                                        new OcrDocCallback(UploadUI.this, displayName, mimeType)
                                );
                            } catch (java.io.IOException e) {
                                android.util.Log.e("OCR", "图片加载失败", e);
                            }
                        });
                        return;
                    }
                }
                
                // 如果提取到文本，在主线程更新UI（增加 null 安全判断）
                if (extractedText != null && !extractedText.trim().isEmpty()) {
                    final String finalText = extractedText;
                    runOnUiThread(() -> {
                        onOcrTextReady(finalText); // 直接写入内容框并联动分类
                        autoFillFields(finalText, displayName, mimeType); // 保持其他字段自动填充
                    });
                }
            } catch (Exception e) {
                android.util.Log.e("UploadUI", "extractDocumentText failed", e);
            }
        }).start();
    }
    
    // PDF文本提取
    private String extractPdfText(Uri uri) throws IOException {
        InputStream inputStream = getContentResolver().openInputStream(uri);
        PDDocument document = PDDocument.load(inputStream);
        PDFTextStripper stripper = new PDFTextStripper();
        
        // 限制提取页数，避免处理时间过长
        int pageCount = document.getNumberOfPages();
        if (pageCount > 10) {
            stripper.setEndPage(10); // 只提取前10页
        }
        
        String text = stripper.getText(document);
        document.close();
        inputStream.close();
        
        return text;
    }
    

    
    // 纯文本文件提取
    private String extractPlainText(Uri uri) throws IOException {
        InputStream inputStream = getContentResolver().openInputStream(uri);
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) != -1) {
            result.write(buffer, 0, length);
        }
        inputStream.close();
        return result.toString("UTF-8");
    }

    private void updatePreviewFromFirstImage() {
        for (Uri u : selectedFiles) {
            String mime = com.example.myapplication.service.UploadService.getMimeType(this, u);
            if (mime != null && mime.startsWith("image/")) {
                if (previewImage != null) previewImage.setImageURI(u);
                currentPreviewUri = u;
                if (fileInfoText != null) {
                    fileInfoText.setText(
                            com.example.myapplication.service.UploadService.getDisplayName(this, u) + "  |  " + mime
                    );
                }
                try {
                    com.google.mlkit.vision.common.InputImage image =
                            com.google.mlkit.vision.common.InputImage.fromFilePath(this, u);
                    com.example.myapplication.service.OCRService.recognize(
                            this,
                            image,
                            new OcrPreviewCallback(UploadUI.this, u, mime)
                    );
                } catch (Exception e) {
                    Utils.toast(this, "读取图片失败：" + e.getMessage());
                }
                return;
            }
        }
        previewImage.setImageDrawable(null);
        currentPreviewUri = null;
    }

    // 命名的 OCR 回调类，替代匿名内部类 $1/$2，改为静态内部类以避免某些 D8 处理内部类元数据时的崩溃
    static class OcrDocCallback implements com.example.myapplication.service.OCRService.Callback {
        private final UploadUI ui;
        private final String displayName;
        private final String mimeType;

        OcrDocCallback(UploadUI ui, String displayName, String mimeType) {
            this.ui = ui;
            this.displayName = displayName;
            this.mimeType = mimeType;
        }

        @Override
        public void onSuccess(String text) {
            ui.runOnUiThread(() -> {
                android.widget.EditText contentEt = ui.findViewById(R.id.contentInput);
                if (contentEt != null) {
                    contentEt.setText(text != null ? text : "");
                }
                ui.onOcrTextReady(text);
                ui.autoFillFields(text, displayName, mimeType);
            });
        }

        @Override
        public void onFailure(Exception e) {
            com.example.myapplication.Utils.toast(ui, "识别失败：" + e.getMessage());
        }
    }

    private static class OcrPreviewCallback implements com.example.myapplication.service.OCRService.Callback {
        private final UploadUI ui;
        private final Uri uri;
        private final String mime;
        OcrPreviewCallback(UploadUI ui, Uri uri, String mime) {
            this.ui = ui;
            this.uri = uri;
            this.mime = mime;
        }
        @Override
        public void onSuccess(String text) {
            android.widget.EditText contentEt = ui.findViewById(R.id.contentInput);
            if (contentEt != null) {
                contentEt.setText((text == null || text.isEmpty()) ? "未识别到文字" : text);
            }
            ui.autoFillFields(text,
                    com.example.myapplication.service.UploadService.getDisplayName(ui, uri),
                    mime);
            ui.onOcrTextReady(text);
        }
        @Override
        public void onFailure(Exception e) {
            Utils.toast(ui, "读取图片失败：" + e.getMessage());
        }
    }

    private String getMimeType(Uri uri) {
        ContentResolver cr = getContentResolver();
        return cr.getType(uri);
    }

    private String getDisplayName(Uri uri) {
        String name = null;
        try (Cursor cursor = getContentResolver()
                .query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null)) {
            if (cursor != null && cursor.moveToFirst()) {
                name = cursor.getString(0);
            }
        } catch (Exception ignored) {}
        return name;
    }

    // 安全方法：避免视图为 null 时崩溃
    private void safeSetText(TextView view, CharSequence text) {
        if (view != null) {
            view.setText(text != null ? text : "");
        }
    }

    private void safeSetSelection(Spinner spinner, int position) {
        if (spinner != null && position >= 0) {
            spinner.setSelection(position);
        }
    }

    private void onOcrTextReady(String ocrText) {
        if (classificationService != null && ocrText != null && ocrText.trim().length() > 0) {
            com.example.myapplication.service.ClassificationService.Result res =
                    classificationService.classifyWithSegmentation(ocrText);
            if (classificationResultText != null) {
                classificationResultText.setVisibility(android.view.View.VISIBLE);
                String confidenceLevel = "";
                if (res.confidence >= 0.8) {
                    confidenceLevel = "高";
                } else if (res.confidence >= 0.6) {
                    confidenceLevel = "中";
                } else if (res.confidence >= 0.4) {
                    confidenceLevel = "低";
                } else {
                    confidenceLevel = "很低";
                }
                classificationResultText.setText(
                        "推荐分类：" + res.bestTitle +
                        "（匹配度" + String.format(java.util.Locale.getDefault(), "%.0f%%", res.confidence * 100) + "）"
                );
            }
            try {
                applyAutoChapterSelection(res);
            } catch (Exception e) {
                android.util.Log.e("UploadUI", "auto chapter match failed", e);
            }

            // 低置信度时静默应用最佳候选，不再弹窗打扰用户
            if (res.confidence < 0.6 && res.topCandidates != null && !res.topCandidates.isEmpty()) {
                // 直接取第一个候选作为推荐结果应用
                com.example.myapplication.service.ClassificationService.Candidate sel = res.topCandidates.get(0);
                autoChapterId = sel.chapterId;
                autoSubjectId = sel.subjectId;
                Spinner subjectSp = findViewById(R.id.subjectSpinner);
                if (subjectSp != null) {
                    com.example.myapplication.repository.CourseRepository cr = new com.example.myapplication.repository.CourseRepository(this);
                    java.util.List<com.example.myapplication.domain.Course> subs = cr.getAllCourses();
                    java.util.List<String> names = new java.util.ArrayList<>();
                    int subjIndex = -1;
                    for (int i = 0; i < subs.size(); i++) {
                        names.add(subs.get(i).getName());
                        if (subs.get(i).getId() == autoSubjectId) subjIndex = i;
                    }
                    android.widget.ArrayAdapter<String> subjAdapter = new android.widget.ArrayAdapter<>(
                            this, android.R.layout.simple_spinner_item, names);
                    subjAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                    subjectSp.setAdapter(subjAdapter);
                    if (subjIndex >= 0) subjectSp.setSelection(subjIndex);
                }
            }
        }
    }

    private void applyAutoChapterSelection(com.example.myapplication.service.ClassificationService.Result res) {
        if (res == null || res.bestTitle == null) return;
        String hint = res.bestTitle.trim();
        com.example.myapplication.repository.CourseRepository cr = new com.example.myapplication.repository.CourseRepository(this);
            java.util.List<com.example.myapplication.domain.Chapter> hits = cr.findChaptersByNameLike(hint, 1);
        if (hits == null || hits.isEmpty()) return;
        com.example.myapplication.domain.Chapter ch = hits.get(0);
        autoChapterId = ch.getId();
        autoSubjectId = (long) ch.getCourseId();
        // 预选中科目下拉
        Spinner subjectSp = findViewById(R.id.subjectSpinner);
        if (subjectSp != null) {
            if (availableSubjects == null || availableSubjects.isEmpty()) {
                String account = com.example.myapplication.service.AuthService.getCurrentUser(this);
                com.example.myapplication.repository.UserRepository ur2 = new com.example.myapplication.repository.UserRepository(this);
                com.example.myapplication.domain.User u2 = ur2.getUserByAccount(account);
                Long majorId = u2 != null ? u2.getMajorId() : null;
                String gradeText = ur2.getGradeText(account);
                availableSubjects = cr.getCoursesByMajorAndGrade(majorId, gradeText);
                if (availableSubjects == null || availableSubjects.isEmpty()) availableSubjects = cr.getCoursesByMajor(majorId);
                if (availableSubjects == null || availableSubjects.isEmpty()) availableSubjects = cr.getAllCourses();
            }
            int subjIndex = -1;
            for (int i = 0; i < availableSubjects.size(); i++) {
                if (availableSubjects.get(i).getId() == autoSubjectId) { subjIndex = i; break; }
            }
            if (subjIndex >= 0) subjectSp.setSelection(subjIndex);
        }
        loadChaptersForSubject(autoSubjectId);
        android.widget.Spinner chapterSp = findViewById(R.id.chapterSpinner);
        if (chapterSp != null && availableChapters != null) {
            int chIndex = -1;
            for (int i = 0; i < availableChapters.size(); i++) if (availableChapters.get(i).getId() == autoChapterId) { chIndex = i; break; }
            if (chIndex >= 0) chapterSp.setSelection(chIndex);
        }
    }

    private void loadChaptersForSubject(long subjectId) {
        com.example.myapplication.repository.CourseRepository cr = new com.example.myapplication.repository.CourseRepository(this);
        availableChapters = cr.getChaptersBySubjectId(subjectId);
        java.util.List<String> names = new java.util.ArrayList<>();
        for (com.example.myapplication.domain.Chapter c : availableChapters) names.add(c.getName());
        android.widget.ArrayAdapter<String> ad = new android.widget.ArrayAdapter<>(this, android.R.layout.simple_spinner_item, names);
        ad.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        android.widget.Spinner chapterSp = findViewById(R.id.chapterSpinner);
        if (chapterSp != null) chapterSp.setAdapter(ad);
    }

    // HanLP增强版分类器（使用真正的HanLP开源代码）
    private static class KeywordClassifier {
        private final Context context;
        private final List<Node> leafNodes = new ArrayList<>();
        private final Set<String> stopwords = new HashSet<>(Arrays.asList(
                "的", "和", "与", "或", "在", "是", "为", "及", "等", "公式", "计算", "方法", "简介"
        ));
        private boolean hanLPInitialized = false;

        static class Node {
            String id;
            String title;
            List<String> keywords = new ArrayList<>();
            List<String> aliases = new ArrayList<>();
        }

        static class Result {
            String bestId;
            String bestTitle;
            double confidence;
            List<Node> topCandidates = new ArrayList<>();
        }

        KeywordClassifier(Context ctx) throws IOException, JSONException {
            this.context = ctx.getApplicationContext();
            loadOutlineFromAssets("course_outline_robotics.json");
            
            // 初始化HanLP
            try {
                // 测试HanLP是否可用
                Log.i("HanLP", "HanLP初始化成功");
            } catch (Exception e) {
                Log.w("HanLP", "HanLP初始化失败，将使用基础分类: " + e.getMessage());
            }
        }

        private void loadOutlineFromAssets(String fileName) throws IOException, JSONException {
            String json = readAssetText(fileName);
            JSONObject root = new JSONObject(json);
            JSONArray nodes = root.getJSONArray("nodes");
            for (int i = 0; i < nodes.length(); i++) {
                JSONObject chapter = nodes.getJSONObject(i);
                parseNodeRecursive(chapter);
            }
        }

        private void parseNodeRecursive(JSONObject obj) throws JSONException {
            String id = obj.optString("id");
            String title = obj.optString("title");
            List<String> keywords = jsonArrayToList(obj.optJSONArray("keywords"));
            List<String> aliases = jsonArrayToList(obj.optJSONArray("aliases"));
            JSONArray children = obj.optJSONArray("children");

            if (children == null || children.length() == 0) {
                Node node = new Node();
                node.id = id;
                node.title = title;
                node.keywords.addAll(keywords);
                node.aliases.addAll(aliases);
                leafNodes.add(node);
            } else {
                Node node = new Node();
                node.id = id;
                node.title = title;
                node.keywords.addAll(keywords);
                node.aliases.addAll(aliases);
                leafNodes.add(node);

                for (int i = 0; i < children.length(); i++) {
                    parseNodeRecursive(children.getJSONObject(i));
                }
            }
        }

        private List<String> jsonArrayToList(JSONArray arr) {
            List<String> list = new ArrayList<>();
            if (arr == null) return list;
            for (int i = 0; i < arr.length(); i++) {
                list.add(arr.optString(i));
            }
            return list;
        }

        private String readAssetText(String fileName) throws IOException {
            AssetManager am = context.getAssets();
            InputStream is = am.open(fileName);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buf = new byte[4096];
            int len;
            while ((len = is.read(buf)) != -1) {
                baos.write(buf, 0, len);
            }
            is.close();
            return baos.toString("UTF-8");
        }
    }
    
    // 重新构建聚合文本（当文件被移除时）
    private void rebuildAggregatedText() {
        // 注意：这是一个简化实现，实际应用中可能需要重新提取所有文件
        // 这里只是清空聚合文本，让用户重新上传文件
        
        // 如果还有文件，提示用户重新处理
        if (!selectedFiles.isEmpty()) {
            fileInfoText.setText("文件已移除，请重新上传以更新内容分析");
        } else {
            fileInfoText.setText("未选择文件");
            // 清空表单
            EditText titleEt = findViewById(R.id.noteTitle);
            EditText descEt = findViewById(R.id.contentInput);
            EditText tagsEt = findViewById(R.id.tagsInput);
            
            if (titleEt != null) titleEt.setText("");
            if (descEt != null) descEt.setText("");
            if (tagsEt != null) tagsEt.setText("");
            
            if (classificationResultText != null) {
                classificationResultText.setVisibility(View.GONE);
            }
        }
    }
    
    // HanLP增强分类器（真正集成HanLP）
    private static class EnhancedKeywordClassifier {
        private final Context context;
        private boolean hanLPInitialized = false;
        
        static class Result {
            String bestId = "";
            String bestTitle = "";
            double confidence = 0.0;
            boolean isSegmented = false;
            List<Object> topCandidates = new ArrayList<>();
        }
        
        EnhancedKeywordClassifier(Context ctx) throws IOException, JSONException {
            this.context = ctx.getApplicationContext();
            // 移除 HanLP 依赖，UI 层仅使用基础分类逻辑
            hanLPInitialized = false;
        }
        
        Result classifyWithSegmentation(String text) {
            Result result = new Result();
            if (text == null || text.trim().isEmpty()) {
                result.bestTitle = "未能判定，建议人工选择章节";
                return result;
            }
            // 仅使用基础分类逻辑，移除 HanLP 相关增强
            result = performBasicClassification(text);
            return result;
        }
        
        private Result performBasicClassification(String text) {
            Result result = new Result();
            String lowerText = text.toLowerCase();
            
            // 机器人学相关关键词匹配
            if (lowerText.contains("机器人") || lowerText.contains("robot") || lowerText.contains("robotics")) {
                result.bestTitle = "第1章 机器人学概述";
                result.confidence = 0.8;
            } else if (lowerText.contains("坐标") || lowerText.contains("变换") || lowerText.contains("transform") || 
                      lowerText.contains("齐次") || lowerText.contains("homogeneous")) {
                result.bestTitle = "第2章 坐标变换与齐次变换";
                result.confidence = 0.75;
            } else if (lowerText.contains("运动学") || lowerText.contains("kinematics") || 
                      lowerText.contains("正运动学") || lowerText.contains("逆运动学")) {
                result.bestTitle = "第3章 机器人运动学";
                result.confidence = 0.8;
            } else if (lowerText.contains("雅可比") || lowerText.contains("jacobian") || 
                      lowerText.contains("速度") || lowerText.contains("velocity")) {
                result.bestTitle = "第4章 雅可比矩阵";
                result.confidence = 0.85;
            } else if (lowerText.contains("动力学") || lowerText.contains("dynamics") || 
                      lowerText.contains("拉格朗日") || lowerText.contains("lagrange")) {
                result.bestTitle = "第5章 机器人动力学";
                result.confidence = 0.8;
            } else if (lowerText.contains("轨迹") || lowerText.contains("路径") || lowerText.contains("trajectory") || 
                      lowerText.contains("规划") || lowerText.contains("planning")) {
                result.bestTitle = "第6章 轨迹规划";
                result.confidence = 0.75;
            } else if (lowerText.contains("控制") || lowerText.contains("control") || 
                      lowerText.contains("pid") || lowerText.contains("反馈")) {
                result.bestTitle = "第7章 机器人控制";
                result.confidence = 0.8;
            } else {
                result.bestTitle = "第1章 机器人学概述";
                result.confidence = 0.5;
            }
            
            return result;
        }
    }
}

