// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title DocumentChain
 * @notice Records and verifies document hashes on the blockchain
 * @dev Provides tamper-proof document verification
 */
contract DocumentChain is Ownable {
    struct DocumentRecord {
        bytes32 id;
        string requestId;
        string documentId;
        string hash;
        uint256 version;
        uint256 timestamp;
        address uploadedBy;
    }

    // Mapping from document ID to its records
    mapping(string => DocumentRecord[]) private _documentHistory;

    // Mapping from document ID to latest hash
    mapping(string => string) private _latestHashes;

    // Mapping from hash to document ID (for reverse lookup)
    mapping(string => string) private _hashToDocument;

    // Counter for unique record IDs
    uint256 private _recordCounter;

    // Events
    event DocumentRecorded(
        bytes32 indexed recordId,
        string indexed requestId,
        string documentId,
        string hash,
        uint256 version,
        uint256 timestamp
    );

    constructor() Ownable(msg.sender) {}

    /**
     * @notice Record a document hash
     * @param requestId The license request ID
     * @param documentId The document ID
     * @param hash The document hash (e.g., SHA-256)
     * @param version The document version
     * @return recordId The unique record ID
     */
    function recordDocumentHash(
        string calldata requestId,
        string calldata documentId,
        string calldata hash,
        uint256 version
    ) public onlyOwner returns (bytes32) {
        require(bytes(requestId).length > 0, "Request ID required");
        require(bytes(documentId).length > 0, "Document ID required");
        require(bytes(hash).length > 0, "Hash required");

        _recordCounter++;
        bytes32 recordId = keccak256(
            abi.encodePacked(documentId, version, block.timestamp, _recordCounter)
        );

        DocumentRecord memory record = DocumentRecord({
            id: recordId,
            requestId: requestId,
            documentId: documentId,
            hash: hash,
            version: version,
            timestamp: block.timestamp,
            uploadedBy: msg.sender
        });

        _documentHistory[documentId].push(record);
        _latestHashes[documentId] = hash;
        _hashToDocument[hash] = documentId;

        emit DocumentRecorded(
            recordId,
            requestId,
            documentId,
            hash,
            version,
            block.timestamp
        );

        return recordId;
    }

    /**
     * @notice Verify if a document hash exists
     * @param documentId The document ID
     * @param hash The hash to verify
     * @return True if the hash matches any recorded version
     */
    function verifyDocumentHash(string calldata documentId, string calldata hash)
        public
        view
        returns (bool)
    {
        DocumentRecord[] memory history = _documentHistory[documentId];

        for (uint256 i = 0; i < history.length; i++) {
            if (keccak256(bytes(history[i].hash)) == keccak256(bytes(hash))) {
                return true;
            }
        }

        return false;
    }

    /**
     * @notice Verify if a hash is the latest version
     * @param documentId The document ID
     * @param hash The hash to verify
     * @return True if the hash is the latest version
     */
    function verifyLatestHash(string calldata documentId, string calldata hash)
        public
        view
        returns (bool)
    {
        return keccak256(bytes(_latestHashes[documentId])) == keccak256(bytes(hash));
    }

    /**
     * @notice Get the complete history of a document
     * @param documentId The document ID
     * @return Array of DocumentRecord structs
     */
    function getDocumentHistory(string calldata documentId)
        public
        view
        returns (DocumentRecord[] memory)
    {
        return _documentHistory[documentId];
    }

    /**
     * @notice Get the latest hash for a document
     * @param documentId The document ID
     * @return The latest document hash
     */
    function getLatestDocumentHash(string calldata documentId)
        public
        view
        returns (string memory)
    {
        return _latestHashes[documentId];
    }

    /**
     * @notice Get document ID by hash
     * @param hash The document hash
     * @return The document ID
     */
    function getDocumentByHash(string calldata hash)
        public
        view
        returns (string memory)
    {
        return _hashToDocument[hash];
    }

    /**
     * @notice Get the version count for a document
     * @param documentId The document ID
     * @return The number of versions
     */
    function getVersionCount(string calldata documentId) public view returns (uint256) {
        return _documentHistory[documentId].length;
    }

    /**
     * @notice Get a specific version of a document
     * @param documentId The document ID
     * @param version The version number (1-indexed)
     * @return The DocumentRecord for that version
     */
    function getDocumentVersion(string calldata documentId, uint256 version)
        public
        view
        returns (DocumentRecord memory)
    {
        require(version > 0 && version <= _documentHistory[documentId].length, "Invalid version");
        return _documentHistory[documentId][version - 1];
    }
}
