package ai.intelliswarm.swarmai.skill;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Quality assessment of a generated skill.
 * Scores across multiple dimensions to inform promotion decisions.
 * Based on the SELF_CHECK.md rubric.
 */
public record SkillQualityScore(
    int documentationScore,     // 0-20: description quality, parameter docs
    int testCoverageScore,      // 0-20: test case count and quality
    int errorHandlingScore,     // 0-20: try/catch, null checks, edge cases
    int codeComplexityScore,    // 0-20: LOC, nesting depth, readability
    int outputFormatScore       // 0-20: consistent output, proper formatting
) {
    public int totalScore() {
        return documentationScore + testCoverageScore + errorHandlingScore
             + codeComplexityScore + outputFormatScore;
    }

    public String grade() {
        int total = totalScore();
        if (total >= 90) return "A";
        if (total >= 80) return "B";
        if (total >= 70) return "C";
        if (total >= 60) return "D";
        return "F";
    }

    public Map<String, Object> toMap() {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("documentation", documentationScore);
        map.put("testCoverage", testCoverageScore);
        map.put("errorHandling", errorHandlingScore);
        map.put("codeComplexity", codeComplexityScore);
        map.put("outputFormat", outputFormatScore);
        map.put("total", totalScore());
        map.put("grade", grade());
        return map;
    }

    /**
     * Assess a generated skill's quality based on its code, instruction body, and metadata.
     * Adapts scoring dimensions based on skill type:
     * - CODE skills: scored on code quality (error handling, complexity, output format)
     * - PROMPT skills: scored on instruction quality (structure, constraints, examples)
     * - HYBRID/COMPOSITE: scored on both dimensions where applicable
     */
    public static SkillQualityScore assess(GeneratedSkill skill) {
        String code = skill.getCode();
        boolean hasCode = code != null && !code.isBlank();
        String normalizedCode = hasCode ? code : "";
        String desc = skill.getDescription();
        String body = skill.getInstructionBody();
        boolean hasBody = body != null && !body.isBlank();
        SkillType skillType = skill.getSkillType();

        // 1. Documentation: description quality + parameter descriptions in schema
        int docScore = 0;
        if (desc != null && desc.length() > 20) docScore += 10;
        if (desc != null && desc.length() > 50) docScore += 5;
        Map<String, Object> schema = skill.getParameterSchema();
        if (schema != null && !schema.isEmpty()) {
            @SuppressWarnings("unchecked")
            Map<String, Object> props = (Map<String, Object>) schema.get("properties");
            if (props != null && !props.isEmpty()) docScore += 5;
        }

        // 2. Test coverage: unit test cases + integration tests
        int testScore = 0;
        int testCount = skill.getTestCases() != null ? skill.getTestCases().size() : 0;
        int integrationTestCount = skill.getIntegrationTests() != null ? skill.getIntegrationTests().size() : 0;

        // Unit test cases (max 10 points)
        if (testCount >= 1) testScore += 5;
        if (testCount >= 2) testScore += 3;
        if (skill.getTestCases() != null) {
            for (String test : skill.getTestCases()) {
                if (test.contains("assert") && test.contains("result")) testScore += 2;
                break; // only check first
            }
        }

        // Integration tests (max 10 points) — valued higher because they verify real behavior
        if (integrationTestCount >= 1) testScore += 5;
        if (integrationTestCount >= 2) testScore += 3;
        if (skill.getIntegrationTests() != null) {
            for (SkillDefinition.IntegrationTest it : skill.getIntegrationTests()) {
                if (it.assertionCode() != null && it.assertionCode().contains("assert")) testScore += 2;
                break; // only check first
            }
        }

        // CODE skills with no tests at all get capped at 0
        if (hasCode && testCount == 0 && integrationTestCount == 0) {
            testScore = 0;
        }

        // PROMPT skills get test credit for having self-check items, examples, or integration tests
        if (!hasCode && hasBody) {
            SkillDefinition def = skill.getDefinition();
            if (def.getSelfCheckItems() != null && !def.getSelfCheckItems().isEmpty()) testScore += 5;
            if (def.getExamples() != null && !def.getExamples().isEmpty()) testScore += 3;
            if (integrationTestCount >= 1) testScore += 2;
            testScore = Math.min(20, testScore);
        }

        // 3. Error handling / Guardrails
        int errorScore = 0;
        if (hasCode) {
            if (normalizedCode.contains("try") && normalizedCode.contains("catch")) errorScore += 10;
            if (normalizedCode.contains("?:") || normalizedCode.contains("!= null") || normalizedCode.contains("?: \"")) errorScore += 5;
            if (normalizedCode.contains("Error:") || normalizedCode.contains("error")) errorScore += 5;
        }
        if (hasBody) {
            // PROMPT skills: constraints and guardrails serve the same purpose as error handling
            SkillDefinition def = skill.getDefinition();
            if (def.getConstraints() != null && !def.getConstraints().isEmpty()) errorScore += 10;
            if (body.toLowerCase().contains("do not") || body.toLowerCase().contains("never") ||
                body.toLowerCase().contains("avoid")) errorScore += 5;
            if (body.toLowerCase().contains("error") || body.toLowerCase().contains("fallback") ||
                body.toLowerCase().contains("if unable")) errorScore += 5;
        }
        errorScore = Math.min(20, errorScore);

        // 4. Structure / Complexity
        int complexityScore = 0;
        if (hasCode) {
            complexityScore = 20;
            int lineCount = normalizedCode.split("\n").length;
            if (lineCount > 50) complexityScore -= 5;
            if (lineCount > 100) complexityScore -= 5;
            int maxNesting = 0, currentNesting = 0;
            for (char c : normalizedCode.toCharArray()) {
                if (c == '{') currentNesting++;
                if (c == '}') currentNesting--;
                maxNesting = Math.max(maxNesting, currentNesting);
            }
            if (maxNesting > 4) complexityScore -= 5;
            if (maxNesting > 6) complexityScore -= 5;
            complexityScore = Math.max(0, complexityScore);
        } else if (hasBody) {
            // PROMPT skills: well-structured instructions score higher
            complexityScore = 10; // base score for having a body
            if (body.contains("#")) complexityScore += 5; // has headings
            if (body.contains("- ") || body.contains("* ") || body.contains("1.")) complexityScore += 5; // has lists/steps
            complexityScore = Math.min(20, complexityScore);
        }

        // 5. Output format: consistent result formatting
        int outputScore = 0;
        if (hasCode) {
            if (normalizedCode.contains("result") || normalizedCode.contains("return") || normalizedCode.contains("output")) outputScore += 10;
            if (normalizedCode.contains("\\n") || normalizedCode.contains("StringBuilder") || normalizedCode.contains("String.format")) outputScore += 5;
            if (normalizedCode.contains("\"Error:") || normalizedCode.contains("catch")) outputScore += 5;
        }
        if (hasBody) {
            SkillDefinition def = skill.getDefinition();
            if (def.getOutputFormat() != null) outputScore += 10;
            if (def.getResources().containsKey("output-template")) outputScore += 5;
            if (body.contains("## Output") || body.contains("output format") ||
                body.toLowerCase().contains("format your response")) outputScore += 5;
        }
        outputScore = Math.min(20, outputScore);

        return new SkillQualityScore(docScore, testScore, errorScore, complexityScore, outputScore);
    }
}
