import * as ts from "typescript"
import * as fs from "fs-extra"

interface PropertyInfo {
    name: string
    type: string
    required?: boolean
    nullable?: boolean
    length?: number
    default?: any
    relation?: string
}

export function getEntityProperties(entityFilePath: string): PropertyInfo[] {
    const sourceFile = ts.createSourceFile(
        entityFilePath,
        fs.readFileSync(entityFilePath, "utf8"),
        ts.ScriptTarget.ESNext,
        true
    )

    const properties: PropertyInfo[] = []
    let propertyInfo: PropertyInfo

    ts.forEachChild(sourceFile, (node) => {
        if (ts.isClassDeclaration(node)) {
            node.members.forEach((member) => {
                if (ts.isPropertyDeclaration(member) && member.name) {
                    const propertyName = member.name.getText(sourceFile)
                    const typeNode = member.type
                    const type = typeNode ? typeNode.getText(sourceFile) : "any"

                    // Extract specific attributes from decorators
                    const decorators = ts.getDecorators(member)
                    propertyInfo = {
                        name: propertyName,
                        type,
                    } as PropertyInfo

                    if (decorators) {
                        decorators.forEach((decorator) => {
                            const expression = decorator.expression
                            if (
                                ts.isCallExpression(expression) &&
                                ts.isIdentifier(expression.expression)
                            ) {
                                const decoratorName =
                                    expression.expression.getText(sourceFile)
                                if (
                                    decoratorName === "Column" ||
                                    decoratorName === "PrimaryGeneratedColumn"
                                ) {
                                    const args = expression.arguments
                                    if (
                                        args.length > 0 &&
                                        ts.isObjectLiteralExpression(args[0])
                                    ) {
                                        args[0].properties.forEach(
                                            (property) => {
                                                if (
                                                    ts.isPropertyAssignment(
                                                        property
                                                    ) &&
                                                    ts.isIdentifier(
                                                        property.name
                                                    )
                                                ) {
                                                    const key =
                                                        property.name.getText(
                                                            sourceFile
                                                        )
                                                    const value =
                                                        property.initializer.getText(
                                                            sourceFile
                                                        )
                                                    if (key === "nullable") {
                                                        propertyInfo.nullable =
                                                            value === "true"
                                                    } else if (
                                                        key === "length"
                                                    ) {
                                                        propertyInfo.length =
                                                            parseInt(value, 10)
                                                    } else if (
                                                        key === "default"
                                                    ) {
                                                        propertyInfo.default = value
                                                    }
                                                    propertyInfo.required =
                                                        !propertyInfo.nullable
                                                }
                                            }
                                        )
                                    }
                                }
                            }
                        })
                    }

                    properties.push(propertyInfo)
                } else if (
                    ts.isPropertyDeclaration(member) &&
                    ts.isIdentifier(member.name)
                ) {
                    const propertyName = member.name.getText(sourceFile)
                    const typeNode = member.type
                    const type = typeNode ? typeNode.getText(sourceFile) : "any"

                    // Check for relation decorators
                    const decorators = ts.getDecorators(member)
                    if (decorators) {
                        decorators.forEach((decorator) => {
                            const expression = decorator.expression
                            if (
                                ts.isCallExpression(expression) &&
                                ts.isIdentifier(expression.expression)
                            ) {
                                const decoratorName =
                                    expression.expression.getText(sourceFile)
                                if (
                                    decoratorName === "OneToMany" ||
                                    decoratorName === "ManyToOne" ||
                                    decoratorName === "OneToOne" ||
                                    decoratorName === "ManyToMany"
                                ) {
                                    propertyInfo.relation = decoratorName
                                }
                            }
                        })
                    }

                    properties.push({ name: propertyName, type })
                }
            })
        }
    })

    return properties
}
